From 13bf0786efbe079020db6d38b4c3c14c465ab7e4 Mon Sep 17 00:00:00 2001 From: Jordan Nelson Date: Mon, 10 Jun 2024 17:19:06 -0400 Subject: [PATCH] chore: supporting missing auth config options --- .../amplify_outputs/auth/auth_outputs.dart | 10 + .../amplify_outputs/auth/oauth_outputs.dart | 35 ++++ .../lib/src/config/auth/auth_config.dart | 11 +- .../amplify_outputs_mapping_test.dart | 189 ++++++++++++++---- 4 files changed, 203 insertions(+), 42 deletions(-) diff --git a/packages/amplify_core/lib/src/config/amplify_outputs/auth/auth_outputs.dart b/packages/amplify_core/lib/src/config/amplify_outputs/auth/auth_outputs.dart index e16cc91830..e4dbe9c566 100644 --- a/packages/amplify_core/lib/src/config/amplify_outputs/auth/auth_outputs.dart +++ b/packages/amplify_core/lib/src/config/amplify_outputs/auth/auth_outputs.dart @@ -5,6 +5,7 @@ import 'package:amplify_core/amplify_core.dart'; import 'package:amplify_core/src/config/amplify_outputs/auth/mfa.dart'; import 'package:amplify_core/src/config/amplify_outputs/auth/oauth_outputs.dart'; import 'package:amplify_core/src/config/amplify_outputs/auth/password_policy.dart'; +import 'package:meta/meta.dart'; part 'auth_outputs.g.dart'; @@ -19,6 +20,7 @@ class AuthOutputs required this.awsRegion, this.userPoolId, this.userPoolClientId, + this.appClientSecret, this.identityPoolId, this.passwordPolicy, this.oauth, @@ -42,6 +44,14 @@ class AuthOutputs /// The Cognito User Pool Client ID. final String? userPoolClientId; + /// A fixed string that must be used in all API requests to the app client + /// if the the app client has one configured. + /// + /// Note: This attribute is not part of the Amplify Outputs schema. + /// This exists for backwards compatibility with the Gen 1 config. + @internal + final String? appClientSecret; + /// The Cognito Identity Pool ID. final String? identityPoolId; diff --git a/packages/amplify_core/lib/src/config/amplify_outputs/auth/oauth_outputs.dart b/packages/amplify_core/lib/src/config/amplify_outputs/auth/oauth_outputs.dart index 5b01b47767..44ed7d6bf9 100644 --- a/packages/amplify_core/lib/src/config/amplify_outputs/auth/oauth_outputs.dart +++ b/packages/amplify_core/lib/src/config/amplify_outputs/auth/oauth_outputs.dart @@ -4,6 +4,7 @@ import 'package:amplify_core/amplify_core.dart'; import 'package:amplify_core/src/config/amplify_outputs/auth/identity_provider.dart'; import 'package:amplify_core/src/config/amplify_outputs/auth/oauth_response_type.dart'; +import 'package:meta/meta.dart'; part 'oauth_outputs.g.dart'; @@ -19,7 +20,11 @@ class OAuthOutputs required this.domain, required this.scopes, required this.redirectSignInUri, + this.redirectSignInUriQueryParameters, required this.redirectSignOutUri, + this.redirectSignOutUriQueryParameters, + this.tokenUri, + this.tokenUriQueryParameters, required this.responseType, }); @@ -38,9 +43,39 @@ class OAuthOutputs /// URIs used to redirect after signing in using an identity provider. final List redirectSignInUri; + /// Optional query params in the sign in URI. + /// + /// Note: This attribute is not part of the Amplify Outputs schema. + /// This exists for backwards compatibility with the Gen 1 config. + @internal + final Map? redirectSignInUriQueryParameters; + /// URIs used to redirect after signing out. final List redirectSignOutUri; + /// Optional query params in the sign in URI. + /// + /// Note: This attribute is not part of the Amplify Outputs schema. + /// This exists for backwards compatibility with the Gen 1 config. + @internal + final Map? redirectSignOutUriQueryParameters; + + /// The OAuth token URI. + /// + /// Defaults to '/oauth2/token'. + /// + /// Note: This attribute is not part of the Amplify Outputs schema. + /// This exists for backwards compatibility with the Gen 1 config. + @internal + final String? tokenUri; + + /// Optional query params in the OAuth token URI. + /// + /// Note: This attribute is not part of the Amplify Outputs schema. + /// This exists for backwards compatibility with the Gen 1 config. + @internal + final Map? tokenUriQueryParameters; + /// {@macro amplify_core.amplify_outputs.oauth_response_type} final OAuthResponseType responseType; diff --git a/packages/amplify_core/lib/src/config/auth/auth_config.dart b/packages/amplify_core/lib/src/config/auth/auth_config.dart index 0f51ffee36..d10eb348ac 100644 --- a/packages/amplify_core/lib/src/config/auth/auth_config.dart +++ b/packages/amplify_core/lib/src/config/auth/auth_config.dart @@ -111,13 +111,19 @@ class AuthConfig extends AmplifyPluginConfigMap { final oAuthConfig = plugin?.oAuth; final identityProviders = plugin?.socialProviders?.map((p) => p.toIdentityProvider()).toList(); - final oauth = oAuthConfig != null && identityProviders != null + final oauth = oAuthConfig != null ? OAuthOutputs( - identityProviders: identityProviders, + identityProviders: identityProviders ?? [], domain: oAuthConfig.webDomain, scopes: oAuthConfig.scopes, redirectSignInUri: oAuthConfig.signInRedirectUri.split(','), + redirectSignInUriQueryParameters: + oAuthConfig.signInUriQueryParameters, redirectSignOutUri: oAuthConfig.signOutRedirectUri.split(','), + redirectSignOutUriQueryParameters: + oAuthConfig.signOutUriQueryParameters, + tokenUri: oAuthConfig.tokenUri, + tokenUriQueryParameters: oAuthConfig.tokenUriQueryParameters, // Amplify Flutter only supports responseType:code // "response_type" is set to "code" by `getAuthorizationUrl` from // pkg:oauth2 @@ -129,6 +135,7 @@ class AuthConfig extends AmplifyPluginConfigMap { awsRegion: region, userPoolId: userPool?.poolId, userPoolClientId: userPool?.appClientId, + appClientSecret: userPool?.appClientSecret, identityPoolId: identityPool?.poolId, passwordPolicy: passwordPolicy, oauth: oauth, diff --git a/packages/amplify_core/test/config/amplify_outputs_mapping/amplify_outputs_mapping_test.dart b/packages/amplify_core/test/config/amplify_outputs_mapping/amplify_outputs_mapping_test.dart index 395acd67ac..592597f84f 100644 --- a/packages/amplify_core/test/config/amplify_outputs_mapping/amplify_outputs_mapping_test.dart +++ b/packages/amplify_core/test/config/amplify_outputs_mapping/amplify_outputs_mapping_test.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:amplify_core/amplify_core.dart'; +import 'package:amplify_core/src/config/amplify_outputs/auth/oauth_outputs.dart'; import 'package:test/test.dart'; import './data/amplify_outputs.g.dart' as outputs; @@ -43,46 +44,7 @@ void main() { }); test('maps config with multiple api plugins', () async { - // hand written config for testing purposes. - const config = ''' - { - "api": { - "plugins": { - "awsAPIPlugin": { - "data1": { - "endpointType": "GraphQL", - "endpoint": "fake-data-url-1", - "region": "us-east-1", - "authorizationType": "AWS_IAM", - "apiKey": "fake-data-api-key" - }, - "data2": { - "endpointType": "GraphQL", - "endpoint": "fake-data-url-2", - "region": "us-east-1", - "authorizationType": "AWS_IAM", - "apiKey": "fake-data-api-key" - }, - "rest1": { - "endpointType": "REST", - "endpoint": "fake-rest-url-1", - "region": "us-east-1", - "authorizationType": "AWS_IAM", - "apiKey": "fake-data-api-key" - }, - "rest2": { - "endpointType": "REST", - "endpoint": "fake-rest-url-2", - "region": "us-east-1", - "authorizationType": "AWS_IAM", - "apiKey": "fake-data-api-key" - } - } - } - } - } - '''; - final configJson = jsonDecode(config) as Map; + final configJson = jsonDecode(multiApiConfig) as Map; final amplifyConfig = AmplifyConfig.fromJson(configJson); final mappedOutputs = amplifyConfig.toAmplifyOutputs(); expect(mappedOutputs.data?.length, 2); @@ -92,9 +54,156 @@ void main() { expect(dataUrls, ['fake-data-url-1', 'fake-data-url-2']); expect(restUrls, ['fake-rest-url-1', 'fake-rest-url-2']); }); + + test('maps config with all oauth options', () async { + final configJson = jsonDecode(oauthConfig) as Map; + final amplifyConfig = AmplifyConfig.fromJson(configJson); + final mappedOutputs = amplifyConfig.toAmplifyOutputs(); + final oauth = mappedOutputs.auth?.oauth as OAuthOutputs; + expect(oauth.redirectSignInUri, containsAll([signInUri1, signInUri2])); + expect( + oauth.redirectSignInUriQueryParameters, + {signInQueryParamKey: signInQueryParamValue}, + ); + expect(oauth.redirectSignOutUri, containsAll([signOutUri1, signOutUri2])); + expect( + oauth.redirectSignOutUriQueryParameters, + {signOutQueryParamKey: signOutQueryParamValue}, + ); + expect(oauth.tokenUri, tokenUri); + expect( + oauth.tokenUriQueryParameters, + {tokenQueryParamKey: tokenQueryParamValue}, + ); + expect(oauth.scopes, containsAll([scope1, scope2])); + }); + + test('maps config with app client secret', () async { + final configJson = jsonDecode(clientSecretConfig) as Map; + final amplifyConfig = AmplifyConfig.fromJson(configJson); + final mappedOutputs = amplifyConfig.toAmplifyOutputs(); + expect(mappedOutputs.auth?.appClientSecret, appClientSecret); + }); }); } +/// hand written config with multiple Rest and GraphQL objects +const multiApiConfig = '''{ + "api": { + "plugins": { + "awsAPIPlugin": { + "data1": { + "endpointType": "GraphQL", + "endpoint": "fake-data-url-1", + "region": "us-east-1", + "authorizationType": "AWS_IAM", + "apiKey": "fake-data-api-key" + }, + "data2": { + "endpointType": "GraphQL", + "endpoint": "fake-data-url-2", + "region": "us-east-1", + "authorizationType": "AWS_IAM", + "apiKey": "fake-data-api-key" + }, + "rest1": { + "endpointType": "REST", + "endpoint": "fake-rest-url-1", + "region": "us-east-1", + "authorizationType": "AWS_IAM", + "apiKey": "fake-data-api-key" + }, + "rest2": { + "endpointType": "REST", + "endpoint": "fake-rest-url-2", + "region": "us-east-1", + "authorizationType": "AWS_IAM", + "apiKey": "fake-data-api-key" + } + } + } + } +} +'''; + +const oAuthDomain = 'fake-web-domain-dev.auth.us-east-1.amazoncognito.com'; +const signInUri1 = 'fake-sign-in-uri-1'; +const signInUri2 = 'fake-sign-in-uri-2'; +const signInQueryParamKey = 'fake-sign-in-query-param-key'; +const signInQueryParamValue = 'fake-sign-in-query-param-value'; +const signOutUri1 = 'fake-sign-ou-uri-1'; +const signOutUri2 = 'fake-sign-out-uri-2'; +const signOutQueryParamKey = 'fake-sign-out-query-param-key'; +const signOutQueryParamValue = 'fake-sign-out-query-param-value'; +const tokenUri = 'fake-token-uri'; +const tokenQueryParamKey = 'fake-token-query-param-key'; +const tokenQueryParamValue = 'fake-token-query-param-value'; +const scope1 = 'scope1'; +const scope2 = 'scope2'; + +/// hand written config with all oauth options including those not par of the +/// AmplifyOutputs schema (SignInURIQueryParameters, SignOutURIQueryParameters, +/// TokenURI, and TokenURIQueryParameters) +const oauthConfig = '''{ + "auth": { + "plugins": { + "awsCognitoAuthPlugin": { + "CognitoUserPool": { + "Default": { + "PoolId": "us-east-fake-pool-id", + "AppClientId": "fake-client-id", + "Region": "us-east-1" + } + }, + "Auth": { + "Default": { + "OAuth": { + "WebDomain": "$oAuthDomain", + "AppClientId": "fake-client-id", + "SignInRedirectURI": "$signInUri1,$signInUri2", + "SignInURIQueryParameters": { + "$signInQueryParamKey": "$signInQueryParamValue" + }, + "SignOutRedirectURI": "$signOutUri1,$signOutUri2", + "SignOutURIQueryParameters": { + "$signOutQueryParamKey": "$signOutQueryParamValue" + }, + "TokenURI": "$tokenUri", + "TokenURIQueryParameters": { + "$tokenQueryParamKey": "$tokenQueryParamValue" + }, + "Scopes": [ + "$scope1", + "$scope2" + ] + } + } + } + } + } + } +}'''; + +const appClientSecret = 'fake-app-client-secret'; + +/// hand written config with app client secret +const clientSecretConfig = '''{ + "auth": { + "plugins": { + "awsCognitoAuthPlugin": { + "CognitoUserPool": { + "Default": { + "PoolId": "us-east-fake-pool-id", + "AppClientId": "fake-client-id", + "AppClientSecret": "$appClientSecret", + "Region": "us-east-1" + } + } + } + } + } +}'''; + /// Updates the Gen 1 Config to work around known issues /// /// Issues: