Skip to content

Commit

Permalink
fix(amplify-provider-awscloudformation): admin token refresh, configu…
Browse files Browse the repository at this point in the history
…re project (#6629)
  • Loading branch information
jhockett authored Feb 16, 2021
1 parent c1366d1 commit 38dab98
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 42 deletions.
1 change: 0 additions & 1 deletion packages/amplify-provider-awscloudformation/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@
"moment": "^2.24.0",
"netmask": "^1.0.6",
"node-fetch": "^2.6.1",
"open": "^7.0.0",
"ora": "^4.0.3",
"promise-sequential": "^1.1.1",
"proxy-agent": "^3.1.1",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import open from 'open';
import ora from 'ora';
import { $TSContext } from 'amplify-cli-core';
import { $TSContext, open } from 'amplify-cli-core';

import { adminVerifyUrl, adminBackendMap, isAmplifyAdminApp } from './utils/admin-helpers';
import { AdminLoginServer } from './utils/admin-login-server';

export async function adminLoginFlow(context: $TSContext, appId: string, envName: string, region?: string) {
export async function adminLoginFlow(context: $TSContext, appId: string, envName?: string, region?: string) {
envName = envName || context.amplify.getEnvInfo().envName;
if (!region) {
const { isAdminApp, region: _region } = await isAmplifyAdminApp(appId);
if (!isAdminApp) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { prompt } from 'inquirer';
import _ from 'lodash';
import path from 'path';
import proxyAgent from 'proxy-agent';
import { adminLoginFlow } from './admin-login';
import awsRegions from './aws-regions';
import constants from './constants';
import setupNewUser from './setup-new-user';
Expand Down Expand Up @@ -346,13 +345,29 @@ async function promptForAuthConfig(context: $TSContext, authConfig?: AuthFlowCon
let answers: $TSAny;

if (availableProfiles && availableProfiles.length > 0) {
const authType = authConfig.type ?? (await askAuthType());
let authType: AuthFlow;
let isAdminApp = false;
if (authConfig?.type) {
authType = authConfig.type;
} else {
try {
const appId = resolveAppId(context);
isAdminApp = (await isAmplifyAdminApp(appId))?.isAdminApp || false;
} catch {
isAdminApp = false;
}
authType = await askAuthType(isAdminApp);
}
if (authType === 'profile') {
printProfileInfo(context);
awsConfigInfo.config.useProfile = true;
answers = await prompt(profileNameQuestion(availableProfiles, awsConfigInfo.config.profileName));
awsConfigInfo.config.profileName = answers.profileName;
return;
} else if (authType === 'admin') {
awsConfigInfo.configLevel = 'amplifyAdmin';
awsConfigInfo.config.useProfile = false;
return;
}
} else {
awsConfigInfo.config.useProfile = false;
Expand Down Expand Up @@ -547,20 +562,16 @@ export async function loadConfigurationForEnv(context: $TSContext, env: string,

const projectConfigInfo = getConfigForEnv(context, env);
const authType = await determineAuthFlow(context, projectConfigInfo);
const { print, usageData } = context;
let awsConfig: AwsSdkConfig;

if (authType.type === 'admin') {
projectConfigInfo.configLevel = 'amplifyAdmin';
appId = appId || authType.appId;
if (!doAdminTokensExist(appId)) {
adminLoginFlow(context, appId, env, authType.region);
}
try {
awsConfig = await getTempCredsWithAdminTokens(appId, print);
} catch (error) {
print.error(`Failed to get credentials: ${error.message || error}`);
await usageData.emitError(error);
awsConfig = await getTempCredsWithAdminTokens(context, appId);
} catch (e) {
context.print.error(`Failed to get credentials: ${e.message || e}`);
await context.usageData.emitError(e);
exitOnNextTick(1);
}
} else if (authType.type === 'profile') {
Expand Down Expand Up @@ -698,12 +709,8 @@ export async function getAwsConfig(context: $TSContext): Promise<AwsConfig> {
}
} else if (awsConfigInfo.configLevel === 'amplifyAdmin') {
const appId = resolveAppId(context);

if (!doAdminTokensExist(appId)) {
await adminLoginFlow(context, appId, context.amplify.getEnvInfo().envName);
}
try {
awsConfig = await getTempCredsWithAdminTokens(appId, context.print);
awsConfig = await getTempCredsWithAdminTokens(context, appId);
} catch (err) {
context.print.error('Failed to fetch Amplify Admin credentials');
throw new Error(err);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { stateManager, $TSContext } from 'amplify-cli-core';
import aws from 'aws-sdk';
import _ from 'lodash';
import fetch from 'node-fetch';
import { adminLoginFlow } from '../admin-login';
import { AdminAuthConfig, AwsSdkConfig, CognitoAccessToken, CognitoIdToken } from './auth-types';

export const adminVerifyUrl = (appId: string, envName: string, region: string): string => {
Expand All @@ -27,8 +28,11 @@ export async function isAmplifyAdminApp(appId: string): Promise<{ isAdminApp: bo
return { isAdminApp: !!appState.appId, region: appState.region };
}

export async function getTempCredsWithAdminTokens(appId: string, print: $TSContext['print']): Promise<AwsSdkConfig> {
const authConfig = await getRefreshedTokens(appId, print);
export async function getTempCredsWithAdminTokens(context: $TSContext, appId: string): Promise<AwsSdkConfig> {
if (!doAdminTokensExist(appId)) {
await adminLoginFlow(context, appId);
}
const authConfig = await getRefreshedTokens(context, appId);
const { idToken, IdentityId, region } = authConfig;
// use tokens to get creds and assign to config
const awsConfig = await getAdminCognitoCredentials(idToken, IdentityId, region);
Expand Down Expand Up @@ -82,17 +86,23 @@ async function getAdminStsCredentials(idToken: CognitoIdToken, region: string):
};
}

export async function getRefreshedTokens(appId: string, print: $TSContext['print']) {
async function getRefreshedTokens(context: $TSContext, appId: string) {
// load token, check expiry, refresh if needed
const authConfig: AdminAuthConfig = stateManager.getAmplifyAdminConfigEntry(appId);

if (isJwtExpired(authConfig.idToken)) {
const refreshedTokens = await refreshJWTs(authConfig, print);
// Refresh stored tokens
authConfig.accessToken.jwtToken = refreshedTokens.AccessToken;
authConfig.idToken.jwtToken = refreshedTokens.IdToken;
authConfig.refreshToken.token = refreshedTokens.RefreshToken;
stateManager.setAmplifyAdminConfigEntry(appId, authConfig);
let refreshedTokens: aws.CognitoIdentityServiceProvider.AuthenticationResultType;
try {
refreshedTokens = (await refreshJWTs(authConfig)).AuthenticationResult;
// Refresh stored tokens
authConfig.accessToken.jwtToken = refreshedTokens.AccessToken;
authConfig.idToken.jwtToken = refreshedTokens.IdToken;
authConfig.refreshToken.token = refreshedTokens.RefreshToken;
stateManager.setAmplifyAdminConfigEntry(appId, authConfig);
} catch {
// Refresh failed, fall back to login
await adminLoginFlow(context, appId, null, authConfig.region);
}
}
return authConfig;
}
Expand All @@ -103,21 +113,15 @@ function isJwtExpired(token: CognitoAccessToken | CognitoIdToken) {
return secSinceEpoch >= expiration - 60;
}

async function refreshJWTs(authConfig: AdminAuthConfig, print: $TSContext['print']) {
async function refreshJWTs(authConfig: AdminAuthConfig) {
const CognitoISP = new aws.CognitoIdentityServiceProvider({ region: authConfig.region });
try {
const result = await CognitoISP.initiateAuth({
AuthFlow: 'REFRESH_TOKEN',
AuthParameters: {
REFRESH_TOKEN: authConfig.refreshToken.token,
},
ClientId: authConfig.accessToken.payload.client_id, // App client id from identityPool
}).promise();
return result.AuthenticationResult;
} catch (e) {
print.error(`Failed to refresh tokens: ${e.message || 'Unknown error occurred'}`);
throw e;
}
return await CognitoISP.initiateAuth({
AuthFlow: 'REFRESH_TOKEN',
AuthParameters: {
REFRESH_TOKEN: authConfig.refreshToken.token,
},
ClientId: authConfig.accessToken.payload.client_id, // App client id from identityPool
}).promise();
}

export const adminBackendMap: {
Expand Down

0 comments on commit 38dab98

Please sign in to comment.