Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /* @flow */
- import PromiseQueue from 'promise-queue-observable';
- import { config as AWSConfig, CognitoIdentityCredentials, CognitoSyncManager } from 'aws-sdk';
- import 'amazon-cognito-js';
- import {
- CognitoUserPool,
- CognitoUser,
- AuthenticationDetails,
- CognitoUserAttribute,
- } from 'amazon-cognito-identity-js';
- export type CognitoAuthenticator$Config = {
- log: boolean | 'warn' | 'error' | 'debug',
- };
- const tryJSONParse = (val: mixed): Object =>
- typeof val === 'object'
- ? val
- : // $FlowIgnore
- do {
- let v;
- try {
- v = JSON.parse(val);
- } catch (e) {
- v = val;
- } finally {
- v; // eslint-disable-line
- }
- };
- export const toAWSAttribute = {
- userLocale: 'locale',
- userEmail: 'email',
- firstName: 'name',
- lastName: 'family_name',
- username: 'preferred_username',
- userPhoneNumber: 'phone_number',
- userAddress: 'address',
- phoneVerified: 'phone_number_verified',
- emailVerified: 'email_verified',
- dealerIdentityID: 'custom:dealerIdentityID',
- company: 'custom: company',
- latitude: 'custom:company_latitude',
- longitude: 'custom:company_longitude',
- };
- export const fromAWSAttribute = {
- email: 'userEmail',
- name: 'firstName',
- family_name: 'lastName',
- locale: 'userLocale',
- preferred_username: 'username',
- phone_number: 'userPhoneNumber',
- email_verified: 'emailVerified',
- phone_number_verified: 'phoneVerified',
- address: 'userAddress',
- 'custom:dealerIdentityID': 'dealerIdentityID',
- 'custom:company': 'company',
- 'custom:company_latitude': 'companyLat',
- 'custom:company_longitude': 'companyLon',
- };
- let Authenticator;
- const handleSynchronizeUserData = (dataManager /* , initialRecords */) =>
- new Promise((resolve, reject) => {
- dataManager.synchronize({
- onSuccess(dataset, newRecords) {
- // if (observer) {
- // observer.publish('data_sync', 'success', dataset, newRecords);
- // }
- resolve(['success', dataset, newRecords]);
- },
- onFailure(error) {
- // console.error('DataSync Error: ', error.message);
- // if (observer) {
- // observer.publish('data_sync', 'error', error.message);
- // }
- resolve(['error', error]);
- },
- onConflict(dataset, conflicts, callback) {
- console.warn('Dataset Conflict! ', conflicts);
- // if (observer) {
- // observer.publish('data_sync', 'conflict', conflicts, callback);
- // }
- resolve(['conflict', dataset, conflicts, callback]);
- },
- observersetDeleted(dataset, datasetName, callback) {
- // console.info('Dataset Deleted: ', datasetName);
- // observer.publish('data_sync', 'deleted', datasetName);
- resolve(['deleted', dataset, datasetName, callback]);
- return callback(true);
- },
- observersetMerged(dataset, datasetNames, callback) {
- // console.info('Dataset Merged: ', datasetNames);
- // observer.publish('data_sync', 'merged', datasetNames);
- resolve(['merged', dataset, datasetNames, callback]);
- return callback(true);
- },
- });
- });
- const handleFormatDataRecords = records =>
- records.reduce((p, c) => {
- p[c.key] = tryJSONParse(c.value);
- return p;
- }, {});
- const handleGetAllRecords = dataManager =>
- new Promise((resolve, reject) => {
- handleSynchronizeUserData(dataManager)
- .then(([result, ...args]) =>
- dataManager.getAllRecords((err, rawRecords) => {
- if (err) {
- console.error('Failed to Get All User Records: ', err.message);
- return reject('Failed to Get User Data Records');
- }
- const dealerIdentityID = dataManager.getIdentityId();
- const records = {
- dealerIdentityID,
- data: handleFormatDataRecords(rawRecords),
- manager: dataManager,
- sync: () => handleSynchronizeUserData(dataManager),
- getAll: () => handleGetAllRecords(dataManager),
- };
- return resolve(records);
- }),
- )
- .catch(err => {
- console.error('Failed to Get User Recors: ', err);
- });
- });
- const getCredentials = (Username: string, Password: string): Class<AuthenticationDetails> =>
- new AuthenticationDetails({
- Username,
- Password,
- });
- const parseUserAttributes = attributes =>
- attributes.reduce((p, c = {}) => {
- const { Name, Value } = c;
- if (fromAWSAttribute[Name]) {
- p[fromAWSAttribute[Name]] = Value;
- } else {
- p[Name] = Value;
- }
- return p;
- }, {});
- const formatUserAttributes = attributes => {
- const formatted = [];
- for (const name of Object.keys(attributes)) {
- formatted.push(
- new CognitoUserAttribute({
- Name: toAWSAttribute[name] || name,
- Value: attributes[name],
- }),
- );
- }
- return formatted;
- };
- const getSessionIDToken = (session): string => session.getIdToken().getJwtToken();
- function publishConfirmedRegistrationCode(user, code: string, observer) {
- // console.info('Submitting User Registration Code: ', code);
- return user.confirmRegistration(String(code), true, (err, result) => {
- if (err) {
- console.error('Failed to Publish Confirmed Registration Code: ', err.message);
- observer.publish('error', err.message);
- } else {
- observer.publish('success', result);
- }
- observer.cancel();
- });
- }
- class CognitoAuthenticator {
- constructor(config?: CognitoAuthenticator$Config = {}) {
- this.config = {
- region: 'us-west-2',
- ...config,
- };
- AWSConfig.region = this.config.region;
- this.setDefaults();
- }
- setDefaults = (): void => {
- this.state = {
- pool: undefined,
- mfa: undefined,
- };
- // AWS Classes / Instances
- this.aws = {
- user: undefined,
- userPool: undefined,
- session: undefined,
- identity: undefined,
- sync: undefined,
- dataManager: undefined,
- };
- };
- setPool = (params): Class<CognitoAuthenticator> => {
- const { ClientId, UserPoolId } = params;
- this.state.pool = params;
- if (ClientId && UserPoolId) {
- this.aws.userPool = new CognitoUserPool(params);
- }
- return this;
- };
- setIdentity = (params): Class<CognitoAuthenticator> => {
- this.state.identity = params;
- return this;
- };
- refreshUserFromStorage = (): Class<CognitoAuthenticator> => {
- if (this.aws.userPool) {
- const user = this.aws.userPool.getCurrentUser();
- if (user) {
- this.aws.user = user;
- }
- }
- return this;
- };
- setUser = (Username: string): Class<CognitoAuthenticator> => {
- if (!this.aws.userPool) {
- throw new Error('You must setup the User Pool before setting the user!');
- } else {
- this.aws.user = new CognitoUser({
- Username,
- Pool: this.aws.userPool,
- });
- }
- return this;
- };
- setSession = (session): Class<CognitoAuthenticator> => {
- if (!session.isValid()) {
- throw new Error('Tried to set an invalid Session on Authenticator');
- }
- this.aws.session = session;
- return this;
- };
- setPassword = (prevPassword: string, newPassword: string): Promise<*> =>
- new Promise((resolve, reject) =>
- this.refreshSessionIfNeeded().then(() =>
- this.getUser().changePassword(prevPassword, newPassword, (err, result) => {
- if (err) {
- console.error('Failed to Set User Password: ', err.message);
- return reject(err.message);
- }
- return resolve(result);
- }),
- ),
- );
- getPool = (required?: boolean = true) => {
- if (this.aws.userPool) {
- return this.aws.userPool;
- }
- if (required) {
- throw new Error('Attempted to Get User Pool before it was setup!');
- }
- };
- getUser = (required?: boolean = true) => {
- if (this.aws.user) {
- return this.aws.user;
- }
- if (required) {
- throw new Error('Attempted to Get User before it was setup!');
- }
- };
- getUsername = (required?: boolean = true) => {
- if (this.aws.user) {
- return this.aws.user.getUsername();
- }
- if (required) {
- throw new Error('Attempted to Get Username before it was setup!');
- }
- };
- getSession = (required?: boolean = true) => {
- if (this.aws.session && typeof this.aws.session.isValid === 'function') {
- return this.aws.session;
- }
- if (required) {
- throw new Error('Attempted to Get User Session before it was setup!');
- }
- };
- getRefreshToken = (): string => this.getSession().getRefreshToken();
- getAccessToken = (): string => getSessionIDToken(this.getSession());
- logout = (): Class<CognitoAuthenticator> => {
- const user = this.getUser(false);
- if (user) {
- user.signOut();
- }
- const userPool = this.aws.userPool;
- const poolParams = this.state.pool;
- this.setDefaults();
- // dont reset the userPool on logout
- this.aws.userPool = userPool;
- this.state.pool = poolParams;
- clearAWSCredentialsCache();
- return this;
- };
- startSignup = (username: string, password: string, attributes: Object) => {
- const observer = new PromiseQueue();
- this.getPool().signUp(username, password, attributes, null, (err, result) => {
- if (err) {
- console.error('Signup Error: ', err.message);
- observer.publish('error', err, this);
- } else {
- this.setUser(username);
- const callback = code => publishConfirmedRegistrationCode(this.getUser(), code, observer);
- observer.publish('verify', result, callback, this);
- }
- });
- return { observer };
- };
- startLogin = (username: string, password: string) => {
- // console.log('Start Login: ', username, password);
- const observer = new PromiseQueue();
- const credentials = getCredentials(username, password);
- clearAWSCredentialsCache();
- const onSuccess = session => {
- observer.publish('success', this.setSession(session).getSession(), this);
- observer.cancel();
- };
- const onFailure = err => {
- console.error('Failed to Login User: ', err.message);
- observer.publish('error', err, this);
- observer.cancel();
- };
- const mfaRequired = data => {
- console.info('Multi-Factor Validation Required: ', data);
- const callback = code => {
- const user = this.getUser();
- user.sendMFACode(String(code), { onSuccess, onFailure, newPasswordRequired });
- };
- observer.publish('verify', data, callback, this);
- };
- const newPasswordRequired = (userAttributes, requiredAttributes) => {
- const data = { userAttributes, requiredAttributes };
- // User was signed up by an admin and must provide new
- // password and required attributes, if any, to complete
- // authentication.
- console.log('New Password Required');
- // the api doesn't accept this field back
- delete userAttributes.email_verified;
- const callback = newPassword => {
- this.getUser().completeNewPasswordChallenge(newPassword, userAttributes, {
- onSuccess,
- onFailure,
- mfaRequired,
- });
- };
- observer.publish('reset_password', data, callback, this);
- };
- this.setUser(username)
- .getUser()
- .authenticateUser(credentials, {
- onSuccess,
- onFailure,
- mfaRequired,
- newPasswordRequired,
- });
- return { observer };
- };
- startForgotPassword = (username: string) => {
- const observer = new PromiseQueue();
- this.setUser(username);
- const onSuccess = () => {
- // console.info('Forgot Password Success!');
- observer.publish('success', this);
- observer.cancel();
- };
- const onFailure = err => {
- console.error('Failed to Reset Password: ', err.message);
- observer.publish('error', err, this);
- observer.cancel();
- };
- const inputVerificationCode = data => {
- console.info('Forgot Info Sent, Verification Required: ', data);
- const callback = (code, newPassword) => {
- this.getUser().confirmPassword(String(code), newPassword, {
- onSuccess,
- onFailure,
- });
- };
- observer.publish('verify', data, callback);
- };
- this.setUser(username)
- .getUser()
- .forgotPassword({
- onSuccess,
- onFailure,
- inputVerificationCode,
- });
- return { observer };
- };
- startSetUserAttributes = (attributes: Object) => {
- const observer = new PromiseQueue();
- const onSuccess = result => {
- observer.publish('success', result);
- observer.cancel();
- };
- const onFailure = err => {
- console.error('Failed to Set User Attributes: ', err.message);
- observer.publish('error', err);
- observer.cancel();
- return err;
- };
- const inputVerificationCode = async result => {
- const deliveries = result.CodeDeliveryDetailsList;
- while (deliveries.length > 0) {
- const delivery = deliveries.shift();
- // eslint-disable-next-line
- await new Promise((resolve, reject) => {
- const callback = code => {
- this.getUser().verifyAttribute(delivery.AttributeName, String(code), {
- onSuccess: success => {
- console.log('Attribute Verified: ', success);
- if (deliveries.length === 0) {
- resolve(onSuccess(success));
- } else {
- resolve();
- }
- },
- onFailure: err => {
- reject(onFailure(err));
- },
- });
- };
- // simulate the same style that is used for standard verification of
- // attributes.
- observer.publish('verify', { CodeDeliveryDetails: delivery }, callback);
- // finish promise
- });
- // handle next delivery
- }
- };
- // eslint-disable-next-line
- this.refreshSessionIfNeeded().then(() => {
- const formatted = formatUserAttributes(attributes);
- if (formatted.length === 0) {
- console.error('Formatted Attributes was Empty');
- observer.publish('error', 'Formatted Attributes was Empty');
- return observer.cancel();
- }
- return this.getUser().updateAttributes(formatted, {
- onSuccess,
- onFailure,
- inputVerificationCode,
- });
- });
- return { observer };
- };
- startVerifyAttribute = (attribute?: string = 'email') => {
- const observer = new PromiseQueue();
- const onSuccess = result => {
- observer.publish('success', result);
- observer.cancel();
- };
- const onFailure = err => {
- observer.publish('error', err);
- observer.cancel();
- };
- const inputVerificationCode = result => {
- const callback = code => {
- if (code) {
- return this.getUser().verifyAttribute(attribute, String(code), {
- onSuccess,
- onFailure,
- });
- }
- return observer.cancel();
- };
- observer.publish('verify', result, callback);
- };
- this.refreshSessionIfNeeded()
- .then(() =>
- this.getUser().getAttributeVerificationCode(attribute, {
- onSuccess,
- onFailure,
- inputVerificationCode,
- }),
- )
- .catch(err => observer.publish('error', err));
- return { observer };
- };
- refreshUserAttributes = (): Promise<*> =>
- new Promise((resolve, reject) =>
- this.refreshSessionIfNeeded().then(() =>
- this.getUser().getUserAttributes((err, attributes) => {
- if (err) {
- console.error('[getUserAttributes] Failed: ', err.message);
- return reject(err.message);
- }
- resolve(parseUserAttributes(attributes));
- }),
- ),
- );
- refreshUserSession = (force?: boolean = false): Promise<*> =>
- new Promise((resolve, reject) =>
- this.getUser().getSession((err, session) => {
- if (err) {
- console.error('Failed to Refresh User Session from Library: ', err.message);
- return reject('Refresh Session Failed');
- }
- if (force || !session.isValid()) {
- // manually set the session then attempt to refresh it
- this.aws.session = session;
- return resolve(this.refreshSession());
- }
- resolve(this.setSession(session).getSession());
- }),
- );
- refreshSessionIfNeeded = (): Promise<*> =>
- new Promise(resolve => {
- const session = this.getSession();
- if (session.isValid()) {
- return resolve(session);
- }
- return resolve(this.refreshSession());
- });
- refreshSession = (): Promise<*> =>
- new Promise((resolve, reject) => {
- this.getUser().refreshSession(this.getRefreshToken(), (err, session) => {
- if (err) {
- console.error('Failed to Refresh User Session: ', err.message);
- return reject(err.message);
- } else if (!session.isValid()) {
- console.error('Refreshed Session is Not Valid!');
- return reject('Failed to Refresh Session');
- }
- return resolve(this.setSession(session).getSession());
- });
- });
- refreshUserDevices = (limit?: number = 30, page = null): Promise<*> =>
- new Promise((resolve, reject) =>
- this.refreshSessionIfNeeded().then(() =>
- this.getUser().listDevices(limit, page, {
- onSuccess: result => resolve(result),
- onFailure: err => {
- console.error('Failed to Refresh User Devices: ', err.message);
- return reject(err.message);
- },
- }),
- ),
- );
- refreshAccessTokensIfNeeded = (): Promise<string> =>
- this.refreshSessionIfNeeded().then(session => getSessionIDToken(session));
- refreshAccessTokens = (): Promise<string> =>
- this.refreshSession().then(session => getSessionIDToken(session));
- refreshIdentityPool = () => {
- const identity = this.state.identity;
- if (!identity) {
- throw new Error(
- 'Tried to refresh Identity Pool before setting its params with setIdentity()!',
- );
- }
- return this.refreshSession()
- .then(session => {
- const token = getSessionIDToken(session);
- const userPool = this.getPool();
- const { region } = this.config;
- this.aws.identity = new CognitoIdentityCredentials({
- ...identity,
- Logins: {
- [`cognito-idp.${region}.amazonaws.com/${userPool.getUserPoolId()}`]: token,
- },
- });
- AWSConfig.credentials = this.aws.identity;
- return AWSConfig.credentials.refreshPromise();
- })
- .catch(err => {
- console.error('Failed to Refresh Identity Pool: ', err.message);
- throw err;
- });
- };
- refreshIdentityPoolIfNeeded = () => {
- const identity = this.state.identity;
- if (!identity) {
- throw new Error(
- 'Tried to refresh Identity Pool before setting its params with setIdentity()!',
- );
- }
- return this.refreshSessionIfNeeded()
- .then(session => {
- const token = getSessionIDToken(session);
- const userPool = this.getPool();
- const { region } = this.config;
- this.aws.identity = new CognitoIdentityCredentials({
- ...identity,
- Logins: {
- [`cognito-idp.${region}.amazonaws.com/${userPool.getUserPoolId()}`]: token,
- },
- });
- AWSConfig.credentials = this.aws.identity;
- return AWSConfig.credentials.refreshPromise();
- })
- .catch(err => {
- console.error('Failed to Refresh Identity Pool: ', err.message);
- throw err;
- });
- };
- removeUserDevice = (deviceKey: string): Promise<string> =>
- new Promise((resolve, reject) =>
- this.refreshSessionIfNeeded().then(() =>
- this.getUser().forgetSpecificDevice(deviceKey, {
- onFailure: err => reject(err.message),
- onSuccess: () => resolve(deviceKey),
- }),
- ),
- );
- refreshUserDataSync = (dataset?: string = 'settings', force?: boolean = false): Promise<*> =>
- new Promise((resolve, reject) => {
- const getAWSCredentials = () =>
- AWSConfig.credentials.get(() => {
- this.aws.sync = new CognitoSyncManager();
- return this.aws.sync.openOrCreateDataset(dataset, (err, dataManager) => {
- if (err) {
- console.error('Failed to Open User Data Set: ', dataset, err.message);
- return reject('Failed to Open UserData');
- }
- console.log(dataManager);
- this.aws.dataManager = dataManager;
- return resolve(handleGetAllRecords(dataManager));
- });
- });
- if (force) {
- return this.refreshIdentityPool().then(() => getAWSCredentials());
- }
- return this.refreshIdentityPoolIfNeeded().then(() => getAWSCredentials());
- });
- }
- export function clearAWSCredentialsCache(): void {
- if (AWSConfig.credentials && AWSConfig.credentials.clearCachedId) {
- AWSConfig.credentials.clearCachedId();
- }
- }
- export function createCognitoAuthenticator(
- config?: CognitoAuthenticator$Config,
- ): Class<CognitoAuthenticator> {
- Authenticator = new CognitoAuthenticator(config);
- return Authenticator;
- }
- export function getCognitoAuthenticator(
- config?: CognitoAuthenticator$Config,
- ): Class<CognitoAuthenticator> {
- if (!Authenticator && config) {
- return createCognitoAuthenticator(config);
- }
- return Authenticator;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement