Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for 1165 #1254

Merged
merged 6 commits into from
Jul 20, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs/media/authentication_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,17 @@ Auth.setPreferredMFA(user, 'SMS');
Auth.setPreferredMFA(user, 'NOMFA');
```

#### Retrieving Current Preferred MFA Type

You can get current preferred MFA type in your code:
```js
import { Auth } from 'aws-amplify';

Auth.getPreferredMFA(user).then((data) => {
console.log('Current prefered MFA type is: ' + data);
})
```

#### Letting User Select MFA Type

When working with multiple MFA Types, you can let the app user select the desired authentication method. `SelectMFAType` UI Component, which is provided with `aws-amplify-react` package, renders a list of available MFA types.
Expand Down
49 changes: 49 additions & 0 deletions packages/auth/__tests__/totp-unit-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,12 @@ jest.mock('amazon-cognito-identity-js/lib/CognitoUser', () => {
callback(null, 'Success');
}

CognitoUser.prototype.getUserData = (callback) => {
callback(null, {
PreferredMfaSetting: 'SMS_MFA'
});
}

return CognitoUser;
});

Expand Down Expand Up @@ -331,10 +337,14 @@ describe('auth unit test', () => {
const auth = new Auth(authOptions);

const spyon = jest.spyOn(CognitoUser.prototype, "setUserMfaPreference");
const spyon2 = jest.spyOn(Auth.prototype, 'getPreferredMFA').mockImplementationOnce(() => {
return Promise.resolve('SMS_MFA')
});
expect(await auth.setPreferredMFA(user, 'TOTP')).toBe('Success');
expect(spyon).toBeCalled();

spyon.mockClear();
spyon2.mockClear();
});

'User has not verified software token mfa'
Expand All @@ -354,12 +364,17 @@ describe('auth unit test', () => {
});
});

const spyon3 = jest.spyOn(Auth.prototype, 'getPreferredMFA').mockImplementationOnce(() => {
return Promise.resolve('SMS_MFA')
});

await auth.setPreferredMFA(user, 'NOMFA');
expect(spyon).toBeCalled();
expect(spyon2).toBeCalled();

spyon.mockClear();
spyon2.mockClear();
spyon3.mockClear();
});

test('totp not setup but MFA chosed, enable sms', async () => {
Expand All @@ -377,12 +392,17 @@ describe('auth unit test', () => {
});
});

const spyon3 = jest.spyOn(Auth.prototype, 'getPreferredMFA').mockImplementationOnce(() => {
return Promise.resolve('NOMFA')
});

await auth.setPreferredMFA(user, 'SMS');
expect(spyon).toBeCalled();
expect(spyon2).toBeCalled();

spyon.mockClear();
spyon2.mockClear();
spyon3.mockClear();
});

test('totp not setup but TOTP chosed', async () => {
Expand All @@ -394,6 +414,9 @@ describe('auth unit test', () => {
}
callback(err, null);
});
const spyon2 = jest.spyOn(Auth.prototype, 'getPreferredMFA').mockImplementationOnce(() => {
return Promise.resolve('SMS_MFA')
});

try {
await auth.setPreferredMFA(user, 'TOTP');
Expand All @@ -403,6 +426,7 @@ describe('auth unit test', () => {
expect(spyon).toBeCalled();

spyon.mockClear();
spyon2.mockClear();
});

test('totp not setup but TOTP chosed', async () => {
Expand All @@ -414,6 +438,9 @@ describe('auth unit test', () => {
}
callback(err, null);
});
const spyon2 = jest.spyOn(Auth.prototype, 'getPreferredMFA').mockImplementationOnce(() => {
return Promise.resolve('SMS_MFA')
});

try {
await auth.setPreferredMFA(user, 'TOTP');
Expand All @@ -424,6 +451,7 @@ describe('auth unit test', () => {
expect(spyon).toBeCalled();

spyon.mockClear();
spyon2.mockClear();
});

test('incorrect mfa type', async () => {
Expand All @@ -435,4 +463,25 @@ describe('auth unit test', () => {
}
});
});

describe('getPreferredMFA test', () => {
test('happy case', async () => {
const auth = new Auth(authOptions);

expect(await auth.getPreferredMFA(user)).toBe('SMS_MFA');
});

test('error case', async () => {
const auth = new Auth(authOptions);

const spyon = jest.spyOn(CognitoUser.prototype, 'getUserData').mockImplementationOnce(callback => {
callback('err', null);
})
try {
await auth.getPreferredMFA(user);
} catch (e) {
expect(e).not.toBeNull();
}
});
});
});
24 changes: 22 additions & 2 deletions packages/auth/src/Auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,8 @@ export default class AuthClass {

/**
* get user current preferred mfa option
* this method doesn't work with totp, we need to deprecate it.
* @deprecated
* @param {CognitoUser} user - the current user
* @return - A promise resolves the current preferred mfa option if success
*/
Expand All @@ -418,15 +420,33 @@ export default class AuthClass {
});
});
}

/**
* get preferred mfa method
* @param {CognitoUser} user - the current cognito user
*/
public getPreferredMFA(user: any): Promise<string> {
return new Promise((res, rej) => {
user.getUserData((err, data) => {
if (err) {
logger.debug('getting preferred mfa failed', err);
rej('getting preferred mfa failed: ' + err);
}
const preferredMFA = data.PreferredMfaSetting || 'NOMFA';
res(preferredMFA);
});
});
}

/**
* set preferred MFA method
* @param {CognitoUser} user - the current Cognito user
* @param {string} mfaMethod - preferred mfa method
* @return - A promise resolve if success
*/
public setPreferredMFA(user : any, mfaMethod : string): Promise<any> {
let smsMfaSettings = null;
public async setPreferredMFA(user : any, mfaMethod : string): Promise<any> {
let smsMfaSettings = await this.getPreferredMFA(user) === 'SMS_MFA'?
{ PreferredMfa : false, Enabled : false } : null;
let totpMfaSettings = {
PreferredMfa : false,
Enabled : false
Expand Down
4 changes: 2 additions & 2 deletions packages/auth/src/types/Auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ export interface AuthOptions {
}

/**
* Details for multi-factor authentication
*/
* Details for multi-factor authentication
*/
export interface MfaRequiredDetails {
challengeName: any,
challengeParameters: any
Expand Down