Quantcast
Channel: CodeSection,代码区,数据库(综合) - CodeSec
Viewing all articles
Browse latest Browse all 6262

Posting Additional Parameters During Authentication With Auth0 In Angular 2.4.1

$
0
0

The other day, I looked at how to provision new Cloudant / CouchDB databases as part of the Auth0 authentication and authorization process . But, after that post, I felt uneasy about trying to keep these two systems - Auth0 and Cloudant - in sync; and I wondered if there was a way I could conditionally change the behavior of the Auth0 Rules engine based on the current login transaction (such as forcing new Cloudant API Keys to be generated). Luckily, we can pass additional parameters through as part of the authentication request. And, these parameters are then made available to the logic of our Auth0 Rules engine on WebTask.io.

Run this demo in my javascript Demos project on GitHub .

When authenticating with a passwordless login, you can invoke the .verifyEmailCode() method in the Auth0 JavaScript client:

auth0.verifyEmailCode( email: email, code: code }, function() { /* ... callback ... */ } );

Now, this doesn't appear to be documented anywhere - at least, nowhere that I could find; but, after experimenting with the authentication process, I discovered that you could actually pass additional, arbitrary key-value pairs in along side the "email" and "code" properties:

auth0.verifyEmailCode( email: email, code: code, // Additional, arbitrary parameters. foo: "bar", hello: "world", things: [ "a", "b", "c" ], stuff: { a: 1 } }, function() { /* ... callback ... */ } );

These additional parameters are then made available in the Auth0 Rules engine as part of the context.request.body collection (alongside several other internal Auth0 properties). These parameters can contain any kind of basic JavaScript data type, be it Strings, Numbers, Booleans, Arrays, or Objects; but, be aware - all "simple" values are reported as Strings. So, for example:

( "hello" ) comes through as ( "hello" ). ( true ) comes through as ( "true" ). ( 3 ) comes through as ( "3" ). ( null ) comes through as ( "" ). ( undefined ) comes through as ( "" ).

I don't know why this is - like I said, I couldn't find any documentation about this feature. But, as long as you're careful with your tripple-equals comparator, everything else should work as expected.

NOTE: If you look at the screenshot of the demo below, I believe all values are reported as "strings" because they are actually posted as individual form fields using a special name notation. Meaning, these values aren't just serialized as JSON (JavaScript Object Notation).

To build on top of my previous Cloudant exploration, I wanted to set up a demo in which I could pass-in an additional authentication value that would flush the app_metadata cache and force a Cloudant database access key to be regenerated. Of course, I don't want to include the entire Cloudant portion of this workflow; so, as a proof-of-concept, I just created an Auth0 Rule that looks for a "params" object in the context.request.body collection; and, injects a message into the User Profile that indicates whether or not a particular authentication flag was received.

function addRequestBodyAnalysis( user, context, callback ) { // Since every Rule runs during the Auth0 authentication / authorization workflow, // there's no implicit way to skip a Rule. But, each login transaction reports the // Client ID making the request. As such, we can explicitly bail out of any Rule // that is meant to be associated with a different Client ID. if ( context.clientID !== "K3zOzhAsrok9mx6yrrwDcbMggFC9QxjZ" ) { return( callback( null, user, context ) ); // If you pass additional key-value pairs in during authentication, the non-core // keys (ie, not the keys like "email", "code", "passcode", etc), are made available // in the Rules engine as part of the Request Body collection. var params = ( context.request.body.params || {} ); // CAUTION: For some reason, all of the "simple values" in the request.context.body // come through as Strings. Arrays are still arrays and Objects are still objects; // but, all of the non-complex values come through as strings. So, for example: // -- // - ( true ) comes through as ( "true" ). // - ( 3 ) comes through as ( "3" ). // - ( null ) comes through as ( "" ). // - ( undefined ) comes through as ( "" ). // -- // So, just be careful about how you use your triple-equals comparison! if ( params.reconnectDatabase === "true" ) { user.bodyAnalysis = "Your database has been reconnected!"; } else { user.bodyAnalysis = "No additional params were provided."; // Move on to next Rule. callback( null, user, context );

I decided to wrap my custom authentication data in a "params" object, rather than having it live directly in the "body" collection since the "body" collection already contains a bunch of Auth0 data. And, by isolating my custom parameters, I believe it will cut down on my chance of accidentally colliding with core Auth0 post properties.

Now, to actually populate this "params" object, I had to update the .verifyEmailCode() method in my Angular 2 authentication service - my injectable Auth0 wrapper - to accept an optional "params" argument. This "params" object is then appended to the post data of the internal Auth0 request:

// Import the core angular services. import * as Auth0 from "auth0"; // Import the application components and services. import { IAppMetadata } from "./authentication.interfaces"; import { IAuthorization } from "./authentication.interfaces"; import { IIdentity } from "./authentication.interfaces"; import { IProfile } from "./authentication.interfaces"; import { IUserMetadata } from "./authentication.interfaces"; export interface IParams { [ key: string ]: any; export class AuthenticationService { private auth0: any; // I initialize the Authentication service. constructor() { this.auth0 = new Auth0({ domain: "bennadel.auth0.com", clientID: "K3zOzhAsrok9mx6yrrwDcbMggFC9QxjZ", responseType: "token" }); // --- // PUBLIC METHODS. // --- // I get the user info / profile for the given, authenticated access token. public getUserInfo( accessToken: string ) : Promise<IProfile> { var promise = new Promise<IProfile>( ( resolve, reject ) : void => { this.auth0.getUserInfo( accessToken, ( error: any, result: IProfile ) : void => { error ? reject( error ) : resolve( result ) );

Viewing all articles
Browse latest Browse all 6262

Trending Articles