Skip to content

Commit

Permalink
chore(auth): hosted ui platforms to use AmplifyOutputs types instead …
Browse files Browse the repository at this point in the history
…of AmplifyConfig (#5273)
  • Loading branch information
NikaHsn authored Aug 14, 2024
1 parent 4e943d7 commit b9172be
Show file tree
Hide file tree
Showing 16 changed files with 147 additions and 111 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:amplify_auth_cognito/src/native_auth_plugin.g.dart';
import 'package:amplify_auth_cognito_dart/src/state/state.dart';
import 'package:amplify_auth_cognito_test/amplify_auth_cognito_test.dart';
import 'package:amplify_auth_integration_test/amplify_auth_integration_test.dart';
import 'package:amplify_core/src/config/amplify_outputs/auth/auth_outputs.dart';
import 'package:amplify_flutter/amplify_flutter.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
Expand All @@ -34,7 +35,7 @@ void main() {

setUp(() async {
dependencyManager = DependencyManager()
..addInstance<CognitoOAuthConfig>(hostedUiConfig)
..addInstance<AuthOutputs>(mockConfig.auth!)
..addInstance<SecureStorageInterface>(MockSecureStorage())
..addInstance<http.Client>(
MockClient((request) {
Expand All @@ -58,7 +59,7 @@ void main() {
argPreferprivatesession,
argBrowserpackagename,
) async {
expect(argUrl, contains(hostedUiConfig.webDomain));
expect(argUrl, contains(mockConfig.auth?.oauth?.domain));
expect(argCallbackurlscheme, testUrlScheme);
expect(argPreferprivatesession, isFalse);
expect(argBrowserpackagename, browserPackage);
Expand Down Expand Up @@ -86,7 +87,7 @@ void main() {
argPreferprivatesession,
argBrowserpackagename,
) async {
expect(argUrl, contains(hostedUiConfig.webDomain));
expect(argUrl, contains(mockConfig.auth?.oauth?.domain));
expect(argCallbackurlscheme, testUrlScheme);
expect(argPreferprivatesession, isFalse);
expect(argBrowserpackagename, browserPackage);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,21 +58,21 @@ class HostedUiPlatformImpl extends io.HostedUiPlatformImpl {
if (!_isMobile) {
return super.signInRedirectUri;
}
return config.signInRedirectUris.firstWhere(
(uri) => uri.scheme != 'https' && uri.scheme != 'http',
orElse: () => _noSuitableRedirect(signIn: true),
);
return authOutputs.oauth!.redirectSignInUri.map(Uri.parse).firstWhere(
(uri) => uri.scheme != 'https' && uri.scheme != 'http',
orElse: () => _noSuitableRedirect(signIn: true),
);
}

@override
Uri get signOutRedirectUri {
if (!_isMobile) {
return super.signOutRedirectUri;
}
return config.signOutRedirectUris.firstWhere(
(uri) => uri.scheme != 'https' && uri.scheme != 'http',
orElse: () => _noSuitableRedirect(signIn: false),
);
return authOutputs.oauth!.redirectSignOutUri.map(Uri.parse).firstWhere(
(uri) => uri.scheme != 'https' && uri.scheme != 'http',
orElse: () => _noSuitableRedirect(signIn: false),
);
}

@override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ void main() {
setUp(() async {
secureStorage = MockSecureStorage();
dependencyManager = DependencyManager()
..addInstance(hostedUiConfig)
..addInstance(mockConfig.auth!)
..addInstance<SecureStorageInterface>(secureStorage)
..addInstance<NativeAuthBridge>(ThrowingNativeBridge());
plugin = AmplifyAuthCognito()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,23 @@
// SPDX-License-Identifier: Apache-2.0

import 'package:amplify_core/amplify_core.dart';

/// Configuration helpers for [CognitoUserPoolConfig].
extension HostedUiJwks on CognitoUserPoolConfig {
/// The JSON Web Key (JWK) URI.
///
/// References:
/// - https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-verifying-a-jwt.html
Uri get jwksUri => Uri.parse(
'https://cognito-idp.$region.amazonaws.com/$poolId/.well-known/jwks.json',
);
}
// ignore: implementation_imports
import 'package:amplify_core/src/config/amplify_outputs/auth/oauth_outputs.dart';

/// Configuration helpers for [CognitoOAuthConfig].
///
/// [Reference](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-userpools-server-contract-reference.html)
extension HostedUiConfig on CognitoOAuthConfig {
/// The parsed [webDomain] URI.
extension HostedUiConfig on OAuthOutputs {
/// The parsed [domain] URI.
///
/// If [webDomain] specifies a scheme, it is honored in requests in the same
/// If [domain] specifies a scheme, it is honored in requests in the same
/// way that it is honored for [signInUri], [tokenUri], and [signOutUri]. If
/// no scheme is specified, it defaults to `https` and [webDomain] is
/// no scheme is specified, it defaults to `https` and [domain] is
/// interpreted as a host string.
Uri get _webDomain {
final uri = Uri.parse(webDomain);
final uri = Uri.parse(domain);
if (uri.hasScheme) return uri;
return Uri(scheme: 'https', host: webDomain);
return Uri(scheme: 'https', host: domain);
}

/// The sign in URI.
Expand All @@ -37,14 +28,17 @@ extension HostedUiConfig on CognitoOAuthConfig {
/// - https://docs.aws.amazon.com/cognito/latest/developerguide/login-endpoint.html
Uri signInUri([AuthProvider? provider]) {
Uri baseUri;
// ignore: invalid_use_of_internal_member
if (this.signInUri != null) {
// ignore: invalid_use_of_internal_member
baseUri = Uri.parse(this.signInUri!);
} else {
baseUri = _webDomain.replace(path: '/oauth2/authorize');
}
return baseUri.replace(
queryParameters: <String, String>{
if (provider != null) 'identity_provider': provider.uriParameter,
// ignore: invalid_use_of_internal_member
...?signInUriQueryParameters,
},
);
Expand All @@ -54,51 +48,43 @@ extension HostedUiConfig on CognitoOAuthConfig {
///
/// References:
/// - https://docs.aws.amazon.com/cognito/latest/developerguide/logout-endpoint.html
Uri get signOutUri {
Uri signOutUri(String userPoolClientId) {
return _webDomain.replace(
path: '/logout',
queryParameters: <String, String>{
// ignore: invalid_use_of_internal_member
...?signOutUriQueryParameters,
'client_id': appClientId,
'client_id': userPoolClientId,
},
);
}

/// The sign in redirect URI to use.
///
/// Throws a [StateError] if there are no URIs registered.
Uri get signInRedirectUri => signInRedirectUris.first;
Uri get signInRedirectUri => Uri.parse(redirectSignInUri.first);

/// The sign out redirect URI to use.
///
/// Throws a [StateError] if there are no URIs registered.
Uri get signOutRedirectUri => signOutRedirectUris.first;
Uri get signOutRedirectUri => Uri.parse(redirectSignOutUri.first);

/// The `token` URI.
///
/// References:
/// - https://docs.aws.amazon.com/cognito/latest/developerguide/token-endpoint.html
Uri get tokenUri {
Uri baseUri;
// ignore: invalid_use_of_internal_member
if (this.tokenUri != null) {
// ignore: invalid_use_of_internal_member
baseUri = Uri.parse(this.tokenUri!);
} else {
baseUri = _webDomain.replace(path: '/oauth2/token');
}
return baseUri.replace(
// ignore: invalid_use_of_internal_member
queryParameters: tokenUriQueryParameters,
);
}

/// The `revoke` URI.
///
/// References:
/// - https://docs.aws.amazon.com/cognito/latest/developerguide/revocation-endpoint.html
Uri get revocationUri => _webDomain.replace(path: '/oauth2/revoke');

/// The `userinfo` URI.
///
/// References:
/// - https://docs.aws.amazon.com/cognito/latest/developerguide/userinfo-endpoint.html
Uri get userInfoUri => _webDomain.replace(path: '/oauth2/userInfo');
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ import 'package:amplify_auth_cognito_dart/src/flows/hosted_ui/hosted_ui_platform
import 'package:amplify_auth_cognito_dart/src/model/hosted_ui/oauth_parameters.dart';
import 'package:amplify_auth_cognito_dart/src/state/state.dart';
import 'package:amplify_core/amplify_core.dart';
// ignore: implementation_imports
import 'package:amplify_core/src/config/amplify_outputs/auth/auth_outputs.dart';
// ignore: implementation_imports
import 'package:amplify_core/src/config/amplify_outputs/auth/oauth_outputs.dart';
import 'package:amplify_secure_storage_dart/amplify_secure_storage_dart.dart';
import 'package:http/http.dart' as http;
import 'package:meta/meta.dart';
Expand All @@ -35,12 +39,18 @@ abstract class HostedUiPlatform implements Closeable {
@protected
HostedUiPlatform.protected(this.dependencyManager);

/// The Hosted UI configuration.
/// The Auth configuration.
@protected
CognitoOAuthConfig get config => dependencyManager.expect();
AuthOutputs get authOutputs {
final authOutputs = dependencyManager.get<AuthOutputs>();
if (authOutputs?.oauth == null || authOutputs?.userPoolClientId == null) {
throw const InvalidAccountTypeException.noUserPool();
}
return authOutputs!;
}

/// The Hosted UI storage keys.
late final HostedUiKeys _keys = HostedUiKeys(config.appClientId);
late final HostedUiKeys _keys = HostedUiKeys(authOutputs.userPoolClientId!);

/// The secure storage plugin.
SecureStorageInterface get _secureStorage => dependencyManager.getOrCreate();
Expand Down Expand Up @@ -114,14 +124,16 @@ abstract class HostedUiPlatform implements Closeable {
);

_authCodeGrant = createGrant(
config,
authOutputs.oauth!, authOutputs.userPoolClientId!,
// ignore: invalid_use_of_internal_member
appClientSecret: authOutputs.appClientSecret,
codeVerifier: codeVerifier,
httpClient: httpClient,
provider: provider,
);
final uri = _authCodeGrant!.getAuthorizationUrl(
redirectUri ?? signInRedirectUri,
scopes: config.scopes,
scopes: authOutputs.oauth?.scopes,
state: state,
);

Expand All @@ -137,7 +149,8 @@ abstract class HostedUiPlatform implements Closeable {
@visibleForTesting
@nonVirtual
Uri getSignOutUri({Uri? redirectUri}) {
final signOutUri = HostedUiConfig(config).signOutUri;
final signOutUri = HostedUiConfig(authOutputs.oauth!)
.signOutUri(authOutputs.userPoolClientId!);

return signOutUri.replace(
queryParameters: <String, String>{
Expand All @@ -152,16 +165,18 @@ abstract class HostedUiPlatform implements Closeable {
@visibleForTesting
@nonVirtual
oauth2.AuthorizationCodeGrant createGrant(
CognitoOAuthConfig config, {
OAuthOutputs oauthOutputs,
String userPoolClientId, {
String? appClientSecret,
AuthProvider? provider,
String? codeVerifier,
http.Client? httpClient,
}) {
return oauth2.AuthorizationCodeGrant(
config.appClientId,
HostedUiConfig(config).signInUri(provider),
HostedUiConfig(config).tokenUri,
secret: config.appClientSecret,
userPoolClientId,
HostedUiConfig(authOutputs.oauth!).signInUri(provider),
HostedUiConfig(authOutputs.oauth!).tokenUri,
secret: appClientSecret,
httpClient: httpClient,
codeVerifier: codeVerifier,

Expand All @@ -177,13 +192,15 @@ abstract class HostedUiPlatform implements Closeable {
@visibleForTesting
@nonVirtual
oauth2.AuthorizationCodeGrant restoreGrant(
CognitoOAuthConfig config, {
OAuthOutputs oauthOutputs,
String userPoolClientId, {
required String state,
required String codeVerifier,
http.Client? httpClient,
}) {
final grant = createGrant(
config,
oauthOutputs,
userPoolClientId,
codeVerifier: codeVerifier,
httpClient: httpClient,
);
Expand All @@ -192,7 +209,7 @@ abstract class HostedUiPlatform implements Closeable {
// Advances the internal state.
..getAuthorizationUrl(
signInRedirectUri,
scopes: config.scopes,
scopes: oauthOutputs.scopes,
state: state,
);
}
Expand Down Expand Up @@ -247,7 +264,8 @@ abstract class HostedUiPlatform implements Closeable {
final parameters = dependencyManager.get<OAuthParameters>();
if (parameters != null) {
authCodeGrant = restoreGrant(
config,
authOutputs.oauth!,
authOutputs.userPoolClientId!,
state: state,
codeVerifier: codeVerifier,
httpClient: httpClient,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

import 'package:amplify_auth_cognito_dart/amplify_auth_cognito_dart.dart';
import 'package:amplify_auth_cognito_dart/src/flows/hosted_ui/hosted_ui_platform.dart';
import 'package:amplify_core/amplify_core.dart';
// ignore: implementation_imports
import 'package:aws_common/src/js/common.dart';
import 'package:path/path.dart' show url;
Expand Down Expand Up @@ -31,15 +30,19 @@ class HostedUiPlatformImpl extends HostedUiPlatform {
}

@override
Uri get signInRedirectUri => config.signInRedirectUris.firstWhere(
(uri) => uri.toString().startsWith(_baseUrl),
orElse: () => _noSuitableRedirect(signIn: true),
Uri get signInRedirectUri => Uri.parse(
authOutputs.oauth!.redirectSignInUri.firstWhere(
(uri) => uri.startsWith(_baseUrl),
orElse: () => _noSuitableRedirect(signIn: true),
),
);

@override
Uri get signOutRedirectUri => config.signOutRedirectUris.firstWhere(
(uri) => uri.toString().startsWith(_baseUrl),
orElse: () => _noSuitableRedirect(signIn: false),
Uri get signOutRedirectUri => Uri.parse(
authOutputs.oauth!.redirectSignOutUri.firstWhere(
(uri) => uri.startsWith(_baseUrl),
orElse: () => _noSuitableRedirect(signIn: false),
),
);

/// Launches the given URL.
Expand Down
Loading

0 comments on commit b9172be

Please sign in to comment.