119 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			119 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict';
 | |
| 
 | |
| var Keypairs = require('@root/keypairs');
 | |
| var PocketId = module.exports;
 | |
| var request = require('./request.js');
 | |
| 
 | |
| var keyJson = window.localStorage.getItem('private.jwk.json');
 | |
| 
 | |
| PocketId.signIdToken = async function (idToken) {
 | |
| 	var pair = await Keypairs.parseOrGenerate({ key: keyJson });
 | |
| 	var jwt = await Keypairs.signJwt({
 | |
| 		jwk: pair.private,
 | |
| 		iss: window.location.protocol + '//' + window.location.hostname,
 | |
| 		exp: '15m',
 | |
| 		claims: {
 | |
| 			contact: ['google:' + idToken]
 | |
| 		}
 | |
| 	});
 | |
| 	return jwt;
 | |
| };
 | |
| 
 | |
| PocketId.auth = {};
 | |
| PocketId.auth.meta = async function ({ email }) {
 | |
| 	var loc = window.location;
 | |
| 	var body = await request({
 | |
| 		method: 'GET',
 | |
| 		url:
 | |
| 			loc.protocol +
 | |
| 			'//' +
 | |
| 			loc.hostname +
 | |
| 			'/api/authn/meta?contact=' +
 | |
| 			'mailto:' +
 | |
| 			email
 | |
| 	});
 | |
| 	return body;
 | |
| };
 | |
| 
 | |
| PocketId.auth.verify = async function ({ scheme, email }) {
 | |
| 	if (!scheme) {
 | |
| 		scheme = 'mailto:';
 | |
| 	}
 | |
| 
 | |
| 	var loc = window.location;
 | |
| 	var body = await request({
 | |
| 		method: 'GET',
 | |
| 		url:
 | |
| 			loc.protocol +
 | |
| 			'//' +
 | |
| 			loc.hostname +
 | |
| 			'/api/authn/verify?contact=' +
 | |
| 			scheme +
 | |
| 			email
 | |
| 	});
 | |
| 
 | |
| 	return body;
 | |
| };
 | |
| 
 | |
| PocketId.auth.consume = async function ({
 | |
| 	email = '',
 | |
| 	receipt = '',
 | |
| 	secret = '',
 | |
| 	count = 0
 | |
| }) {
 | |
| 	var loc = window.location;
 | |
| 	var resp = await request({
 | |
| 		method: 'GET',
 | |
| 		url:
 | |
| 			loc.protocol +
 | |
| 			'//' +
 | |
| 			loc.hostname +
 | |
| 			'/api/authn/consume?contact=' +
 | |
| 			(email ? 'mailto:' + email : '') +
 | |
| 			'&receipt=' +
 | |
| 			receipt +
 | |
| 			'&secret=' +
 | |
| 			secret
 | |
| 	});
 | |
| 
 | |
| 	if (resp.body.success) {
 | |
| 		// There should be a token here
 | |
| 		// (or the pubkey should have been given beforehand)
 | |
| 		return resp.body;
 | |
| 	}
 | |
| 
 | |
| 	if (resp.body.error) {
 | |
| 		// TODO special errors are hard failures
 | |
| 	}
 | |
| 
 | |
| 	if (count > 600) {
 | |
| 		throw new Error('abandoned login');
 | |
| 	}
 | |
| 
 | |
| 	return timeout(5000).then(function () {
 | |
| 		console.log('check otp again');
 | |
| 		return PocketId.auth.consume({
 | |
| 			email,
 | |
| 			secret,
 | |
| 			receipt,
 | |
| 			count: count || 0
 | |
| 		});
 | |
| 	});
 | |
| };
 | |
| 
 | |
| async function timeout(ms) {
 | |
| 	return new Promise(function (resolve) {
 | |
| 		setTimeout(resolve, ms);
 | |
| 	});
 | |
| }
 | |
| 
 | |
| var textEncoder = new TextEncoder();
 | |
| PocketId.genKey = async function ({ email }) {
 | |
| 	// Ideally we'd use PBKDF2 or better but... web standards...
 | |
| 	// TODO put a random salt
 | |
| 	var emailU8 = textEncoder.encode(email);
 | |
| 	var salt = await crypto.subtle.digest('SHA-256', emailU8);
 | |
| 	var u8 = textEncoder.encode(answer);
 | |
| 	var hash = await crypto.subtle.digest('SHA-256', u8);
 | |
| };
 |