Skip to content

Commit aa9d525

Browse files
fix(cli-internal): correct OIDC TTL conversion and handle clientId field
fix(cli-internal): correct OIDC TTL conversion and handle clientId field
1 parent 77f6761 commit aa9d525

3 files changed

Lines changed: 142 additions & 6 deletions

File tree

packages/amplify-cli/src/__tests__/commands/gen2-migration/generate/amplify/data/data.generator.test.ts

Lines changed: 90 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ describe('DataGenerator', () => {
390390
`);
391391
});
392392

393-
it('renders OIDC auth mode', async () => {
393+
it('renders OIDC auth mode with TTL converted from milliseconds to seconds', async () => {
394394
const gen1App = await createGen1App({
395395
providers: { awscloudformation: { StackName: 'amplify-test-main-123456', Region: 'us-east-1' } },
396396
api: {
@@ -412,8 +412,8 @@ describe('DataGenerator', () => {
412412
name: 'MyOIDC',
413413
issuerUrl: 'https://example.com',
414414
clientId: 'client123',
415-
authTTL: 3600,
416-
iatTTL: 7200,
415+
authTTL: 3600000,
416+
iatTTL: 7200000,
417417
},
418418
},
419419
],
@@ -459,6 +459,93 @@ describe('DataGenerator', () => {
459459
`);
460460
});
461461

462+
it('renders OIDC auth mode without clientId when absent and supplements from live API', async () => {
463+
const gen1App = await createGen1App({
464+
providers: { awscloudformation: { StackName: 'amplify-test-main-123456', Region: 'us-east-1' } },
465+
api: {
466+
testApi: {
467+
service: 'AppSync',
468+
output: { GraphQLAPIIdOutput: 'api-123' },
469+
},
470+
},
471+
});
472+
mockSchema(gen1App, 'type Todo @model { id: ID! }', ['Todo']);
473+
jest.spyOn(gen1App, 'resourceMetaOutput').mockImplementation((_resource: DiscoveredResource, key: string) => {
474+
if (key === 'GraphQLAPIIdOutput') return 'api-123';
475+
if (key === 'authConfig')
476+
return {
477+
defaultAuthentication: {
478+
authenticationType: 'OPENID_CONNECT',
479+
openIDConnectConfig: {
480+
name: 'NoClientIdProvider',
481+
issuerUrl: 'https://idp.example.com',
482+
authTTL: 1800000,
483+
iatTTL: 3600000,
484+
},
485+
},
486+
} as any;
487+
return undefined as any;
488+
});
489+
jest.spyOn(gen1App.aws, 'fetchGraphqlApi').mockResolvedValue({
490+
apiId: 'api-123',
491+
name: 'testApi',
492+
openIDConnectConfig: { issuer: 'https://idp.example.com', clientId: 'supplemented-client-id' },
493+
additionalAuthenticationProviders: [],
494+
});
495+
496+
const generator = new DataGenerator(gen1App, backendGenerator, outputDir, dataResource, logger);
497+
const ops = await generator.plan();
498+
await ops[0].execute();
499+
500+
const output = writtenFile('resource.ts');
501+
expect(output).toContain("clientId: 'supplemented-client-id'");
502+
expect(output).toContain('tokenExpiryFromAuthInSeconds: 1800');
503+
expect(output).toContain('tokenExpireFromIssueInSeconds: 3600');
504+
});
505+
506+
it('floors TTL values when not exact multiples of 1000', async () => {
507+
const gen1App = await createGen1App({
508+
providers: { awscloudformation: { StackName: 'amplify-test-main-123456', Region: 'us-east-1' } },
509+
api: {
510+
testApi: {
511+
service: 'AppSync',
512+
output: { GraphQLAPIIdOutput: 'api-123' },
513+
},
514+
},
515+
});
516+
mockSchema(gen1App, 'type Todo @model { id: ID! }', ['Todo']);
517+
jest.spyOn(gen1App, 'resourceMetaOutput').mockImplementation((_resource: DiscoveredResource, key: string) => {
518+
if (key === 'GraphQLAPIIdOutput') return 'api-123';
519+
if (key === 'authConfig')
520+
return {
521+
defaultAuthentication: {
522+
authenticationType: 'OPENID_CONNECT',
523+
openIDConnectConfig: {
524+
name: 'Floored',
525+
issuerUrl: 'https://idp.example.com',
526+
clientId: 'abc',
527+
authTTL: 3599999,
528+
iatTTL: 7200500,
529+
},
530+
},
531+
} as any;
532+
return undefined as any;
533+
});
534+
jest.spyOn(gen1App.aws, 'fetchGraphqlApi').mockResolvedValue({
535+
apiId: 'api-123',
536+
name: 'testApi',
537+
additionalAuthenticationProviders: [],
538+
});
539+
540+
const generator = new DataGenerator(gen1App, backendGenerator, outputDir, dataResource, logger);
541+
const ops = await generator.plan();
542+
await ops[0].execute();
543+
544+
const output = writtenFile('resource.ts');
545+
expect(output).toContain('tokenExpiryFromAuthInSeconds: 3599');
546+
expect(output).toContain('tokenExpireFromIssueInSeconds: 7200');
547+
});
548+
462549
it('renders Lambda auth mode', async () => {
463550
const gen1App = await createGen1App({
464551
providers: { awscloudformation: { StackName: 'amplify-test-main-123456', Region: 'us-east-1' } },

packages/amplify-cli/src/commands/gen2-migration/generate/amplify/data/data.generator.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ export class DataGenerator implements Planner {
169169
const hasAdditionalAuthProviders =
170170
graphqlApi.additionalAuthenticationProviders !== undefined && graphqlApi.additionalAuthenticationProviders.length > 0;
171171
const hasAuth = this.gen1App.categoryMeta('auth') !== undefined;
172-
const authorizationModes = this.gen1App.resourceMetaOutput(this.resource, 'authConfig');
172+
const authorizationModes = supplementOidcConfig(this.gen1App.resourceMetaOutput(this.resource, 'authConfig'), graphqlApi);
173173
const hasIamAuth = this.detectIamAuth(authorizationModes, graphqlApi);
174174
const vtlFiles = this.findResolverVtlFiles(this.resource.resourceName);
175175
const hasResolvers = vtlFiles.length > 0;
@@ -275,3 +275,42 @@ export class DataGenerator implements Planner {
275275
return graphqlApi.additionalAuthenticationProviders?.some((p) => p.authenticationType === 'AWS_IAM') ?? false;
276276
}
277277
}
278+
279+
/**
280+
* Supplements the authConfig from amplify-meta.json with OIDC clientId
281+
* values from the live GraphQL API when they are missing in the local config.
282+
*
283+
* Gen1 amplify-meta.json may not include the `clientId` field for OIDC
284+
* providers (depends on CLI version), but the live API always has it.
285+
*/
286+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- untyped authConfig from amplify-meta.json
287+
function supplementOidcConfig(authConfig: any, graphqlApi: GraphqlApi): any {
288+
if (!authConfig) return authConfig;
289+
290+
const result = JSON.parse(JSON.stringify(authConfig));
291+
292+
if (
293+
result.defaultAuthentication?.authenticationType === 'OPENID_CONNECT' &&
294+
result.defaultAuthentication.openIDConnectConfig &&
295+
!result.defaultAuthentication.openIDConnectConfig.clientId &&
296+
graphqlApi.openIDConnectConfig?.clientId
297+
) {
298+
result.defaultAuthentication.openIDConnectConfig.clientId = graphqlApi.openIDConnectConfig.clientId;
299+
}
300+
301+
if (result.additionalAuthenticationProviders) {
302+
for (const provider of result.additionalAuthenticationProviders) {
303+
if (provider.authenticationType !== 'OPENID_CONNECT' || !provider.openIDConnectConfig || provider.openIDConnectConfig.clientId) {
304+
continue;
305+
}
306+
const match = graphqlApi.additionalAuthenticationProviders?.find(
307+
(p) => p.authenticationType === 'OPENID_CONNECT' && p.openIDConnectConfig?.issuer === provider.openIDConnectConfig.issuerUrl,
308+
);
309+
if (match?.openIDConnectConfig?.clientId) {
310+
provider.openIDConnectConfig.clientId = match.openIDConnectConfig.clientId;
311+
}
312+
}
313+
}
314+
315+
return result;
316+
}

packages/amplify-cli/src/commands/gen2-migration/generate/amplify/data/data.renderer.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -489,9 +489,19 @@ export class DataRenderer {
489489
];
490490
if (cfg.clientId) props.push(factory.createPropertyAssignment('clientId', factory.createStringLiteral(cfg.clientId)));
491491
if (cfg.authTTL)
492-
props.push(factory.createPropertyAssignment('tokenExpiryFromAuthInSeconds', factory.createNumericLiteral(cfg.authTTL.toString())));
492+
props.push(
493+
factory.createPropertyAssignment(
494+
'tokenExpiryFromAuthInSeconds',
495+
factory.createNumericLiteral(Math.floor(Number(cfg.authTTL) / 1000).toString()),
496+
),
497+
);
493498
if (cfg.iatTTL)
494-
props.push(factory.createPropertyAssignment('tokenExpireFromIssueInSeconds', factory.createNumericLiteral(cfg.iatTTL.toString())));
499+
props.push(
500+
factory.createPropertyAssignment(
501+
'tokenExpireFromIssueInSeconds',
502+
factory.createNumericLiteral(Math.floor(Number(cfg.iatTTL) / 1000).toString()),
503+
),
504+
);
495505
target.push(factory.createPropertyAssignment('oidcAuthorizationMode', factory.createObjectLiteralExpression(props)));
496506
}
497507

0 commit comments

Comments
 (0)