diff --git a/packages/shorebird_cli/lib/src/auth/auth.dart b/packages/shorebird_cli/lib/src/auth/auth.dart index 764ab484..b6a8c67b 100644 --- a/packages/shorebird_cli/lib/src/auth/auth.dart +++ b/packages/shorebird_cli/lib/src/auth/auth.dart @@ -22,7 +22,7 @@ final authRef = create(Auth.new); Auth get auth => read(authRef); typedef ObtainAccessCredentials = Future Function( - AuthProvider authProvider, + oauth2.AuthProvider authProvider, oauth2.ClientId clientId, List scopes, http.Client client, @@ -30,7 +30,7 @@ typedef ObtainAccessCredentials = Future Function( ); typedef RefreshCredentials = Future Function( - AuthProvider authProvider, + oauth2.AuthProvider authProvider, oauth2.ClientId clientId, oauth2.AccessCredentials credentials, http.Client client, @@ -168,7 +168,7 @@ class Auth { } Future loginCI( - AuthProvider authProvider, { + oauth2.AuthProvider authProvider, { required void Function(String) prompt, }) async { final client = http.Client(); @@ -198,7 +198,7 @@ class Auth { } Future login( - AuthProvider authProvider, { + oauth2.AuthProvider authProvider, { required void Function(String) prompt, }) async { if (_credentials != null) { @@ -330,7 +330,7 @@ extension OauthAuthProvider on Jwt { } } -extension OauthValues on AuthProvider { +extension OauthValues on oauth2.AuthProvider { oauth2.ClientId get clientId { switch (runtimeType) { case oauth2.GoogleAuthProvider: diff --git a/packages/shorebird_cli/test/src/auth/auth_test.dart b/packages/shorebird_cli/test/src/auth/auth_test.dart index 2f949a29..2d80e521 100644 --- a/packages/shorebird_cli/test/src/auth/auth_test.dart +++ b/packages/shorebird_cli/test/src/auth/auth_test.dart @@ -2,7 +2,7 @@ import 'dart:convert'; import 'dart:io' hide Platform; import 'package:cli_util/cli_util.dart'; -import 'package:googleapis_auth/googleapis_auth.dart'; +import 'package:googleapis_auth/googleapis_auth.dart' as oauth2; import 'package:http/http.dart' as http; import 'package:jwt/jwt.dart' show Jwt, JwtPayload; import 'package:mason_logger/mason_logger.dart'; @@ -22,7 +22,7 @@ import 'package:test/test.dart'; import '../fakes.dart'; import '../mocks.dart'; -class FakeProvider extends AuthProvider { +class FakeProvider extends oauth2.AuthProvider { @override Uri get authorizationEndpoint => Uri.https('example.com'); @@ -66,8 +66,8 @@ void main() { group('JwtClaims', () { group('email', () { test('returns null when idToken is not a valid jwt', () { - final credentials = AccessCredentials( - AccessToken( + final credentials = oauth2.AccessCredentials( + oauth2.AccessToken( 'Bearer', 'accessToken', DateTime.now().add(const Duration(minutes: 10)).toUtc(), @@ -142,18 +142,18 @@ void main() { const idToken = '''eyJhbGciOiJIUzI1NiIsImtpZCI6IjEyMzQiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiI1MjMzMDIyMzMyOTMtZWlhNWFudG0wdGd2ZWsyNDB0NDZvcmN0a3RpYWJyZWsuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiI1MjMzMDIyMzMyOTMtZWlhNWFudG0wdGd2ZWsyNDB0NDZvcmN0a3RpYWJyZWsuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMjM0NSIsImhkIjoic2hvcmViaXJkLmRldiIsImVtYWlsIjoidGVzdEBlbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiaWF0IjoxMjM0LCJleHAiOjY3ODl9.MYbITALvKsGYTYjw1o7AQ0ObkqRWVBSr9cFYJrvA46g'''; const email = 'test@email.com'; - const user = User(id: 42, email: email); + const user = User(id: 42, email: email, authProvider: AuthProvider.google); const refreshToken = ''; const scopes = []; final googleAuthProvider = GoogleAuthProvider(); final microsoftAuthProvider = MicrosoftAuthProvider(); - final accessToken = AccessToken( + final accessToken = oauth2.AccessToken( 'Bearer', 'accessToken', DateTime.now().add(const Duration(minutes: 10)).toUtc(), ); - final accessCredentials = AccessCredentials( + final accessCredentials = oauth2.AccessCredentials( accessToken, refreshToken, scopes, @@ -244,7 +244,7 @@ void main() { ), ); - final onRefreshCredentialsCalls = []; + final onRefreshCredentialsCalls = []; final client = AuthenticatedClient.token( token: token, @@ -262,7 +262,7 @@ void main() { expect( onRefreshCredentialsCalls, equals([ - isA() + isA() .having((c) => c.idToken, 'token', idToken), ]), ); @@ -279,7 +279,7 @@ void main() { HttpStatus.ok, ), ); - final onRefreshCredentialsCalls = []; + final onRefreshCredentialsCalls = []; final client = AuthenticatedClient.token( token: token, httpClient: httpClient, @@ -318,9 +318,9 @@ void main() { const expiredIdToken = '''eyJhbGciOiJIUzI1NiIsImtpZCI6IjEyMzQiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiI1MjMzMDIyMzMyOTMtZWlhNWFudG0wdGd2ZWsyNDB0NDZvcmN0a3RpYWJyZWsuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiI1MjMzMDIyMzMyOTMtZWlhNWFudG0wdGd2ZWsyNDB0NDZvcmN0a3RpYWJyZWsuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMjM0NSIsImhkIjoic2hvcmViaXJkLmRldiIsImVtYWlsIjoidGVzdEBlbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiaWF0IjoxMjM0LCJleHAiOjY3ODl9.MYbITALvKsGYTYjw1o7AQ0ObkqRWVBSr9cFYJrvA46g'''; - final onRefreshCredentialsCalls = []; - final expiredCredentials = AccessCredentials( - AccessToken( + final onRefreshCredentialsCalls = []; + final expiredCredentials = oauth2.AccessCredentials( + oauth2.AccessToken( 'Bearer', 'accessToken', DateTime.now().subtract(const Duration(minutes: 1)).toUtc(), @@ -346,7 +346,7 @@ void main() { expect( onRefreshCredentialsCalls, equals([ - isA() + isA() .having((c) => c.idToken, 'token', idToken), ]), ); @@ -363,7 +363,7 @@ void main() { HttpStatus.ok, ), ); - final onRefreshCredentialsCalls = []; + final onRefreshCredentialsCalls = []; final client = AuthenticatedClient.credentials( credentials: accessCredentials, httpClient: httpClient, @@ -431,7 +431,7 @@ void main() { () async { final client = auth.client; expect(client, isA()); - expect(client, isNot(isA())); + expect(client, isNot(isA())); }); }); diff --git a/packages/shorebird_code_push_client/test/src/code_push_client_test.dart b/packages/shorebird_code_push_client/test/src/code_push_client_test.dart index 1f60de2d..49ed7e44 100644 --- a/packages/shorebird_code_push_client/test/src/code_push_client_test.dart +++ b/packages/shorebird_code_push_client/test/src/code_push_client_test.dart @@ -89,7 +89,11 @@ void main() { }); group('getCurrentUser', () { - const user = User(id: 123, email: 'tester@shorebird.dev'); + const user = User( + id: 123, + email: 'tester@shorebird.dev', + authProvider: AuthProvider.google, + ); test('makes the correct request', () async { codePushClient.getCurrentUser().ignore(); @@ -1110,6 +1114,7 @@ void main() { id: 1, email: 'tester@shorebird.dev', displayName: userName, + authProvider: AuthProvider.microsoft, ); test('makes the correct request', () async { diff --git a/packages/shorebird_code_push_protocol/lib/src/models/auth_provider.dart b/packages/shorebird_code_push_protocol/lib/src/models/auth_provider.dart new file mode 100644 index 00000000..4a8819a9 --- /dev/null +++ b/packages/shorebird_code_push_protocol/lib/src/models/auth_provider.dart @@ -0,0 +1,8 @@ +/// The authentication provider used to sign in the user. +enum AuthProvider { + /// The user authenticated using their Google account with our GCP project. + google, + + /// The user authenticated with their Azure/Entra account. + microsoft, +} diff --git a/packages/shorebird_code_push_protocol/lib/src/models/models.dart b/packages/shorebird_code_push_protocol/lib/src/models/models.dart index 12007495..5a812989 100644 --- a/packages/shorebird_code_push_protocol/lib/src/models/models.dart +++ b/packages/shorebird_code_push_protocol/lib/src/models/models.dart @@ -1,5 +1,6 @@ export 'app.dart'; export 'app_metadata.dart'; +export 'auth_provider.dart'; export 'channel.dart'; export 'error_response.dart'; export 'patch.dart'; diff --git a/packages/shorebird_code_push_protocol/lib/src/models/user.dart b/packages/shorebird_code_push_protocol/lib/src/models/user.dart index 235913e0..65573f6f 100644 --- a/packages/shorebird_code_push_protocol/lib/src/models/user.dart +++ b/packages/shorebird_code_push_protocol/lib/src/models/user.dart @@ -1,4 +1,5 @@ import 'package:json_annotation/json_annotation.dart'; +import 'package:shorebird_code_push_protocol/shorebird_code_push_protocol.dart'; part 'user.g.dart'; @@ -11,6 +12,7 @@ class User { const User({ required this.id, required this.email, + required this.authProvider, this.hasActiveSubscription = false, this.displayName, this.stripeCustomerId, @@ -36,4 +38,7 @@ class User { /// The user's Stripe customer ID, if they have one. final String? stripeCustomerId; + + /// The SSO provider used to create the user. + final AuthProvider authProvider; } diff --git a/packages/shorebird_code_push_protocol/lib/src/models/user.g.dart b/packages/shorebird_code_push_protocol/lib/src/models/user.g.dart index 9a57f1c6..11fd2e2e 100644 --- a/packages/shorebird_code_push_protocol/lib/src/models/user.g.dart +++ b/packages/shorebird_code_push_protocol/lib/src/models/user.g.dart @@ -15,6 +15,8 @@ User _$UserFromJson(Map json) => $checkedCreate( final val = User( id: $checkedConvert('id', (v) => v as int), email: $checkedConvert('email', (v) => v as String), + authProvider: $checkedConvert( + 'auth_provider', (v) => $enumDecode(_$AuthProviderEnumMap, v)), hasActiveSubscription: $checkedConvert( 'has_active_subscription', (v) => v as bool? ?? false), displayName: $checkedConvert('display_name', (v) => v as String?), @@ -24,6 +26,7 @@ User _$UserFromJson(Map json) => $checkedCreate( return val; }, fieldKeyMap: const { + 'authProvider': 'auth_provider', 'hasActiveSubscription': 'has_active_subscription', 'displayName': 'display_name', 'stripeCustomerId': 'stripe_customer_id' @@ -36,4 +39,10 @@ Map _$UserToJson(User instance) => { 'display_name': instance.displayName, 'has_active_subscription': instance.hasActiveSubscription, 'stripe_customer_id': instance.stripeCustomerId, + 'auth_provider': _$AuthProviderEnumMap[instance.authProvider]!, }; + +const _$AuthProviderEnumMap = { + AuthProvider.google: 'google', + AuthProvider.microsoft: 'microsoft', +}; diff --git a/packages/shorebird_code_push_protocol/test/src/models/user_test.dart b/packages/shorebird_code_push_protocol/test/src/models/user_test.dart index 5bfa2e1c..3489b230 100644 --- a/packages/shorebird_code_push_protocol/test/src/models/user_test.dart +++ b/packages/shorebird_code_push_protocol/test/src/models/user_test.dart @@ -9,6 +9,7 @@ void main() { email: 'test@shorebird.dev', stripeCustomerId: 'test-customer-id', displayName: 'Test User', + authProvider: AuthProvider.google, ); expect( User.fromJson(user.toJson()).toJson(),