Initial commit
This commit is contained in:
		
							
								
								
									
										190
									
								
								signin.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								signin.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,190 @@
 | 
			
		||||
"use strict";
 | 
			
		||||
 | 
			
		||||
const cl = global.botanLoader;
 | 
			
		||||
const Dragonfly = global.Dragonfly;
 | 
			
		||||
 | 
			
		||||
const crypto = require( "crypto" );
 | 
			
		||||
const util = require( "util" );
 | 
			
		||||
const EventEmitter = require( "events" ).EventEmitter;
 | 
			
		||||
const http = require( "http" );
 | 
			
		||||
const HttpRequest = cl.load( "botanss.net.HttpRequest" );
 | 
			
		||||
 | 
			
		||||
class SignInEventArgs
 | 
			
		||||
{
 | 
			
		||||
	constructor({ message, redirect, data = [], success = false } = {})
 | 
			
		||||
	{
 | 
			
		||||
		this.success = success;
 | 
			
		||||
		this._message = message;
 | 
			
		||||
		this._redirect = redirect;
 | 
			
		||||
		this.data = data
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	get message()
 | 
			
		||||
	{
 | 
			
		||||
		return this._message;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	get location()
 | 
			
		||||
	{
 | 
			
		||||
		return this._redirect + "?" + this.data.join( "&" );
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class Signin extends EventEmitter
 | 
			
		||||
{
 | 
			
		||||
	constructor( conf )
 | 
			
		||||
	{
 | 
			
		||||
		super();
 | 
			
		||||
		this.conf = conf;
 | 
			
		||||
		this.spec = null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Start( action, params, handler )
 | 
			
		||||
	{
 | 
			
		||||
		var request = new HttpRequest( this.conf.spec );
 | 
			
		||||
		var _self = this;
 | 
			
		||||
 | 
			
		||||
		if( this.spec )
 | 
			
		||||
		{
 | 
			
		||||
			_self[ action ]( params, handler );
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			request.addListener( "RequestComplete", function( sender, e )
 | 
			
		||||
			{
 | 
			
		||||
				if( e.statusCode == 200 )
 | 
			
		||||
				{
 | 
			
		||||
					_self.spec = JSON.parse( e.ResponseString );
 | 
			
		||||
					_self[ action ]( params, handler );
 | 
			
		||||
				}
 | 
			
		||||
			} );
 | 
			
		||||
			request.Send();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ValidateAzureAD( params, handler )
 | 
			
		||||
	{
 | 
			
		||||
		if( !this.spec )
 | 
			
		||||
			throw new Error( "OpenID spec is not present" );
 | 
			
		||||
 | 
			
		||||
		var _self = this;
 | 
			
		||||
 | 
			
		||||
		var jwt = params[ "id_token" ];
 | 
			
		||||
		if( !jwt )
 | 
			
		||||
		{
 | 
			
		||||
			handler( this, new SignInEventArgs({
 | 
			
		||||
				"message": params[ "error_description" ]
 | 
			
		||||
			}) );
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var [ jHeader, jPayload, jSig ] = jwt.split( "." );
 | 
			
		||||
 | 
			
		||||
		var header = JSON.parse( Buffer.from( jHeader, "base64" ) );
 | 
			
		||||
		var payload = JSON.parse( Buffer.from( jPayload, "base64" ) );
 | 
			
		||||
 | 
			
		||||
		var aud = payload[ "aud" ];
 | 
			
		||||
		var clientId = this.conf.authorization_endpoint_params.client_id;
 | 
			
		||||
 | 
			
		||||
		if( aud !== clientId )
 | 
			
		||||
		{
 | 
			
		||||
			handler( this, new SignInEventArgs({ message: `aud mismatched: ${aud}` }) );
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var [ url, data ] = this._endPointRes( "jwks_uri" );
 | 
			
		||||
 | 
			
		||||
		var request = new HttpRequest( url + "?" + data.join( "&" ) );
 | 
			
		||||
		request.addListener( "RequestComplete", function( sender, e )
 | 
			
		||||
		{
 | 
			
		||||
			var eArgs;
 | 
			
		||||
 | 
			
		||||
			if( e.statusCode == 200 )
 | 
			
		||||
			{
 | 
			
		||||
				var respJson = JSON.parse( e.ResponseString );
 | 
			
		||||
				var keyId = header[ "kid" ];
 | 
			
		||||
 | 
			
		||||
				for( let key of respJson[ "keys" ] )
 | 
			
		||||
				{
 | 
			
		||||
					if( key[ "kid" ] === keyId )
 | 
			
		||||
					{
 | 
			
		||||
						var hashFunc = crypto.createVerify({
 | 
			
		||||
							"RS256": "RSA-SHA256"
 | 
			
		||||
						}[ header[ "alg" ] ]);
 | 
			
		||||
 | 
			
		||||
						hashFunc.write( `${jHeader}.${jPayload}` );
 | 
			
		||||
						hashFunc.end();
 | 
			
		||||
 | 
			
		||||
						var pub = crypto.createPublicKey({
 | 
			
		||||
							"key": {
 | 
			
		||||
								"kty": key["kty"]
 | 
			
		||||
								, "n": key["n"]
 | 
			
		||||
								, "e": key["e"]
 | 
			
		||||
							}, "format": "jwk"
 | 
			
		||||
						});
 | 
			
		||||
 | 
			
		||||
						eArgs = new SignInEventArgs(
 | 
			
		||||
						{
 | 
			
		||||
							"success": hashFunc.verify( pub, jSig, "base64" )
 | 
			
		||||
							, "data": payload
 | 
			
		||||
						});
 | 
			
		||||
						break;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				eArgs = eArgs || new SignInEventArgs({
 | 
			
		||||
					"message": `Unable to find key: ${keyId}`
 | 
			
		||||
				});
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				eArgs = new SignInEventArgs({
 | 
			
		||||
					"message": `Remote returned ${e.statusCode}`
 | 
			
		||||
				});
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			handler( _self, eArgs );
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		request.Send();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Authorize( params, handler )
 | 
			
		||||
	{
 | 
			
		||||
		var [ url, data ] = this._endPointRes( "authorization_endpoint" );
 | 
			
		||||
		var eArgs = new SignInEventArgs({ "redirect": url, "data": data });
 | 
			
		||||
		handler( this, eArgs );
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_endPointRes( endpoint, handler )
 | 
			
		||||
	{
 | 
			
		||||
		if( !this.spec )
 | 
			
		||||
			throw new Error( "OpenID spec is not present" );
 | 
			
		||||
 | 
			
		||||
		var url = this.spec[ endpoint ];
 | 
			
		||||
		if( !url )
 | 
			
		||||
			throw new Error( "No such endpoint" );
 | 
			
		||||
 | 
			
		||||
		var params = this.conf[ endpoint + "_params" ];
 | 
			
		||||
		var requestData = [];
 | 
			
		||||
 | 
			
		||||
		if( params )
 | 
			
		||||
		{
 | 
			
		||||
			for( var name in params )
 | 
			
		||||
			{
 | 
			
		||||
				var val = params[ name ];
 | 
			
		||||
 | 
			
		||||
				if( val[0] == "{" )
 | 
			
		||||
				{
 | 
			
		||||
					val = this[ val.replace( "{", "" ).replace( "}", "" ) ]();
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				requestData.push( name + "=" + encodeURIComponent( val ) );
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return [ url, requestData ];
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = Signin;
 | 
			
		||||
		Reference in New Issue
	
	Block a user