Skip to content
This repository has been archived by the owner on Feb 24, 2018. It is now read-only.

How to implement login with OTP functionality using aws cognito JS SDK? #625

Open
joe455 opened this issue Dec 18, 2017 · 19 comments
Open

Comments

@joe455
Copy link

joe455 commented Dec 18, 2017

As part of my requirements,I crated sample app which confirms both Email and Password and using MFA too. Now, I want to build functionality like login with OTP.

  1. First on clicking button I will ask them to enter Email / phone.
  2. Next I want to send the OTP and navigate them to profile on success case.

Can somebody give a basic idea how can I implement this?
(I am developing this app Angular 4 and please note that my Email/phone attributes are already verified!!!)

@itrestian
Copy link
Contributor

There is an example in Tim Hunt's re:invent presentation of how to do this using the custom authentication flow (he is the last presenter):

https://www.youtube.com/watch?v=8DDIxqIW1sM

@joe455
Copy link
Author

joe455 commented Dec 19, 2017

Hi @itrestian I understood it but sample app/doc will make me bit more clear. Can you please provide that?

@joe455
Copy link
Author

joe455 commented Dec 19, 2017

@itrestian I tried custom auth flow.But getting error: Incorrect username or password. My code is below.

`let authenticationData = {
            Username: username,
            Password: ''  
        };
        let authenticationDetails = new AuthenticationDetails(authenticationData);
        let userData = {
            Username: username,
            Pool: this.cognitoUtil.getUserPool()
        };
        let cognitoUser = new CognitoUser(userData);
        console.log("cognitoUser",cognitoUser);
        cognitoUser.setAuthenticationFlowType('CUSTOM_AUTH');
        cognitoUser.authenticateUser(authenticationDetails,{
            onSuccess: (result) => {
                console.log("sucuess");
            },
            onFailure: (err) => {
                console.log("err",err);
            },
            customChallenge: (ChallengeParameters) => {
                var challengeResponses = 'challenge-answer'
                cognitoUser.sendCustomChallengeAnswer(challengeResponses, (err,result) => {

                });
            }
        })

    }`

May be this error because of password, But It should not expect the pass word in passwordless authentication flow.

@itrestian
Copy link
Contributor

Actually you would have to call initiateAuth such as in use case 25 in the readme. The authenticateUser call is used for SRP.

@joe455
Copy link
Author

joe455 commented Dec 19, 2017

I tried that but I am getting error like "Property 'initiateAuth' does not exist on type 'CognitoUser'. @itrestian
I am using same lambda functions form tim's video.

@itrestian
Copy link
Contributor

You're probably using an older version of this?

@joe455
Copy link
Author

joe455 commented Dec 19, 2017

I am using 1.28.0 version.
Is it something issue from lambda function?
I am using same lambda functions form tim's video.

@joe455
Copy link
Author

joe455 commented Dec 19, 2017

My lambda functions.

DefineAuth:

`exports.handler = (event, context, callback) => {
    // TODO implement
    console.log("In lambda");
    //callback(null, 'Hello from Lambda');
    if(event.request.session.length === 0){
        event.response.issueTokens = false;
        event.response.failAuthentication = false;
        event.response.challengeName = 'CUSTOM_CHALLENGE'
    } else if(event.request.session.length === 1
    && event.request.session[0].challengeName === 'CUSTOM_CHALLENGE'
    && event.request.session[0].challengeResult === true){
        event.response.issueTokens = true;
        event.response.failAuthentication = false;
    } else {
        event.response.issueTokens = false;
        event.response.failAuthentication = true;
    }
    callback(null, event);
};
`
createAuth:
`console.log("Loading Function");
let AWS = require("aws-sdk");
exports.handler = (event, context, callback) => {
    // TODO implement
    if(event.request.session.length === 0
    && event.request.challengeName === 'CUSTOM_CHALLENGE'){
        //CreateCode
        let answer = Math.random().toString(10).substr(2,6);
        
        //send the code via amazon SNS Global SMS
        let sns = new AWS.SNS();
        sns.publish({
            Message: answer + 'is your login code',
            PhoneNumber: event.request.userAttributes.phone_number
        }, (err, data) => {
            if(err){
                console.log(err.stack);
                return;
            }
            console.log('SMS Sent');
        });
        
        //Set the return parameters --Including correct answer
        event.response.publicChallengeParameters = {};
        event.response.privateChallengeParameters = {};
        event.response.privateChallengeParameters.answer = answer;
        event.response.challengeMetaData = 'PASSWORD_CHALLENGE';
    }
    callback(null, event);
};`

VerifyAuth:
exports.handler = (event, context, callback) => { // TODO implement if(event.request.privateChallengeParameters.answer === event.request.challengeAnswer){ event.response.answerCorrect = true; }else { event.response.answerCorrect = true; } callback(null, event); };

@itrestian
Copy link
Contributor

If you are using the typescript typings, I guess the definition for initiateAuth might not be there yet.

@joe455
Copy link
Author

joe455 commented Dec 19, 2017

What does it mean? I am using angular4 with Typescript

@joe455
Copy link
Author

joe455 commented Dec 19, 2017

`declare module "amazon-cognito-identity-js" {

import * as AWS from "aws-sdk";

export type NodeCallback<E,T> = (err?: E, result?: T) => void;

export interface IAuthenticationDetailsData {
    Username: string;
    Password: string;
}

export class AuthenticationDetails {
    constructor(data: IAuthenticationDetailsData);

    public getUsername(): string;
    public getPassword(): string;
    public getValidationData(): any[];
}

export interface ICognitoStorage {
    setItem(key: string, value: string): void;
    getItem(key: string): string;
    removeItem(key: string): void;
    clear(): void;
}

export interface ICognitoUserData {
    Username: string;
    Pool: CognitoUserPool;
    Storage?: ICognitoStorage;
}

export class CognitoUser {
    constructor(data: ICognitoUserData);

    public setSignInUserSession(signInUserSession: CognitoUserSession): void;
    public getSignInUserSession(): CognitoUserSession | null;
    public getUsername(): string;

    public getAuthenticationFlowType(): string;
    public setAuthenticationFlowType(authenticationFlowType: string): string;

    public getSession(callback: Function): any;
    public refreshSession(refreshToken: CognitoRefreshToken, callback: NodeCallback<any, any>): void;
    public authenticateUser(authenticationDetails: AuthenticationDetails,
                            callbacks: {
                                onSuccess: (session: CognitoUserSession, userConfirmationNecessary?: boolean) => void,
                                onFailure: (err: any) => void,
                                newPasswordRequired?: (userAttributes: any, requiredAttributes: any) => void,
                                mfaRequired?: (challengeName: any, challengeParameters: any) => void,
                                customChallenge?: (challengeParameters: any) => void
                            }): void;
    public confirmRegistration(code: string, forceAliasCreation: boolean, callback: NodeCallback<any, any>): void;
    public sendCustomChallengeAnswer(answerChallenge: any, callback:NodeCallback<any, any>):void;
    public resendConfirmationCode(callback: NodeCallback<Error, "SUCCESS">): void;
    public changePassword(oldPassword: string, newPassword: string, callback: NodeCallback<Error, "SUCCESS">): void;
    public forgotPassword(callbacks: { onSuccess: (data: any) => void, onFailure: (err: Error) => void, inputVerificationCode?: (data: any) => void }): void;
    public confirmPassword(verificationCode: string, newPassword: string, callbacks: { onSuccess: () => void, onFailure: (err: Error) => void }): void;
    public setDeviceStatusRemembered(callbacks: { onSuccess: (success: string) => void, onFailure: (err: any) => void }): void;
    public setDeviceStatusNotRemembered(callbacks: { onSuccess: (success: string) => void, onFailure: (err: any) => void }): void;
    public getDevice(callbacks: {onSuccess: (success: string) => void, onFailure: (err: Error) => void}): any;
    public sendMFACode(confirmationCode: string, callbacks: { onSuccess: (session: CognitoUserSession) => void, onFailure: (err: any) => void }): void;
    public completeNewPasswordChallenge(newPassword: string,
                                        requiredAttributeData: any,
                                        callbacks: {
                                            onSuccess: (session: CognitoUserSession) => void,
                                            onFailure: (err: any) => void,
                                            mfaRequired?: (challengeName: any, challengeParameters: any) => void,
                                            customChallenge?: (challengeParameters: any) => void
                                        }): void;
    public signOut(): void;
    public globalSignOut(callbacks: { onSuccess: (msg: string) => void, onFailure: (err: Error) => void }): void;
    public verifyAttribute(attributeName: string, confirmationCode: string, callbacks: { onSuccess: (success: string) => void, onFailure: (err: Error) => void }): void;
    public getUserAttributes(callback: NodeCallback<Error, CognitoUserAttribute[]>): void;
    public updateAttributes(attributes: ICognitoUserAttributeData[], callback: NodeCallback<Error,string>): void;
    public deleteAttributes(attributeList: string[], callback: NodeCallback<Error, string>): void;
    public getAttributeVerificationCode(name: string, callback: { onSuccess: () => void, onFailure: (err: Error) => void, inputVerificationCode: (data: string) => void | null }): void;
    public deleteUser(callback: NodeCallback<Error, string>): void;
    public enableMFA(callback: NodeCallback<Error, string>): void;
    public disableMFA(callback: NodeCallback<Error, string>): void;
    public getMFAOptions(callback: NodeCallback<Error, MFAOption[]>): void;
}

export interface MFAOption {
    DeliveryMedium: "SMS" |"EMAIL";
    AttributeName: string;
}

export interface ICognitoUserAttributeData {
    Name: string;
    Value: string;
}

export class CognitoUserAttribute {
    constructor(data: ICognitoUserAttributeData);

    public getValue(): string;
    public setValue(value: string): CognitoUserAttribute;
    public getName(): string;
    public setName(name: string): CognitoUserAttribute;
    public toString(): string;
    public toJSON(): Object;
}

export interface ISignUpResult {
    user: CognitoUser;
    userConfirmed: boolean;
    userSub: string;
}

export interface ICognitoUserPoolData {
    UserPoolId: string;
    ClientId: string;
    endpoint?: string;
    Storage?: ICognitoStorage;
}

export class CognitoUserPool {
    constructor(data: ICognitoUserPoolData);

    public getUserPoolId(): string;
    public getClientId(): string;

    public signUp(username: string, password: string, userAttributes: CognitoUserAttribute[], validationData: CognitoUserAttribute[], callback: NodeCallback<Error,ISignUpResult>): void;

    public getCurrentUser(): CognitoUser | null;
}

export interface ICognitoUserSessionData {
    IdToken: CognitoIdToken;
    AccessToken: CognitoAccessToken;
    RefreshToken?: CognitoRefreshToken;
}

export class CognitoUserSession {
    constructor(data: ICognitoUserSessionData);

    public getIdToken(): CognitoIdToken;
    public getRefreshToken(): CognitoRefreshToken;
    public getAccessToken(): CognitoAccessToken;
    public isValid(): boolean;
}

export class CognitoIdentityServiceProvider {
    public config: AWS.CognitoIdentityServiceProvider.Types.ClientConfiguration;
}

export class CognitoAccessToken {
    constructor({ AccessToken }: { AccessToken: string });

    public getJwtToken(): string;
    public getExpiration(): number;
}

export class CognitoIdToken {
    constructor({ IdToken }: { IdToken: string });

    public getJwtToken(): string;
    public getExpiration(): number;
}

export class CognitoRefreshToken {
    constructor({ RefreshToken }: { RefreshToken: string });

    public getToken(): string;
}

export interface ICookieStorageData {
    domain: string;
    path?: string;
    expires?: number;
    secure?: boolean;
}
export class CookieStorage implements ICognitoStorage {
    constructor(data: ICookieStorageData);
    setItem(key: string, value: string): void;
    getItem(key: string): string;
    removeItem(key: string): void;
    clear(): void;
}

}
`
index.d.ts file not consisting of initiateAuth method.

@joe455
Copy link
Author

joe455 commented Dec 19, 2017

I added some thing like below in index.d.ts file. Now my error is Unreconizable lambda output.
public initiateAuth(authenticationDetails: AuthenticationDetails, callbacks: { onSuccess: (session: CognitoUserSession, userConfirmationNecessary?: boolean) => void, onFailure: (err: any) => void, newPasswordRequired?: (userAttributes: any, requiredAttributes: any) => void, mfaRequired?: (challengeName: any, challengeParameters: any) => void, customChallenge?: (challengeParameters: any) => void }): void;

What is happening here?

@joe455
Copy link
Author

joe455 commented Dec 19, 2017

@itrestian kindly let me the exact issue here.

@itrestian
Copy link
Contributor

Not entirely sure, the lambdas look ok to me. You can check the lambda console to debug lambda failures and see lambda inputs and outputs.

@joe455
Copy link
Author

joe455 commented Dec 21, 2017

@itrestian Is any example available for this flow?
Somebody please help me. :(

@itrestian
Copy link
Contributor

The video I mentioned should have the example you need.

@joe455
Copy link
Author

joe455 commented Dec 22, 2017

Yes.
But anyway I am not having intiateAuth method yet!!
Waiting...

@joe455
Copy link
Author

joe455 commented Dec 28, 2017

@itrestian Can you please provide any example achieve custom authentication flow?
Not the video One. The methods used in the video are not there in the current version SDK.
Or Please host the example shown in video somewhere. I will debug it.

@diegolacarta
Copy link

any updates on updated typings? there are a few methods missing

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants