Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,49 @@ final String name = client.clientName ?? client.clientId;
`admin.oauth.createClient` always have one. Clients that register themselves through the dynamic
client registration API, where the name is optional, may not.

### `OAuthClient.tokenEndpointAuthenticationMethod` and `registrationType` are nullable

Auth leaves `token_endpoint_auth_method` and `registration_type` out of a client response when they
are empty, so `OAuthClient.tokenEndpointAuthenticationMethod` is a `String?` and
`OAuthClient.registrationType` is an `OAuthClientRegistrationType?`.

```dart
// Before
final OAuthClientRegistrationType type = client.registrationType;

// After
final OAuthClientRegistrationType? type = client.registrationType;
```

### OAuth clients no longer have a `scope`

Auth does not store a scope on an OAuth client, so `OAuthClient.scope`,
`CreateOAuthClientOptions.scope` and `UpdateOAuthClientOptions.scope` are gone. `OAuthClient.scope`
was always `null`, and Auth ignored the scope sent when creating or updating a client.

Scopes belong to each authorization request instead. A client asks for them with the `scope`
query parameter on `/oauth/authorize`, and the scopes it asked for are on
`OAuthAuthorizationDetailsResponse.scope`.

```dart
// Before
await supabase.auth.admin.oauth.createClient(
CreateOAuthClientOptions(
clientName: 'My App',
redirectUris: ['https://example.com/callback'],
scope: 'openid email',
),
);

// After
await supabase.auth.admin.oauth.createClient(
CreateOAuthClientOptions(
clientName: 'My App',
redirectUris: ['https://example.com/callback'],
),
);
```

### `JWK.publicKey` returns a `JWTKey`

`JWK.publicKey` supports EC keys as well as RSA keys, so it returns a `JWTKey`: an `RSAPublicKey`
Expand Down
36 changes: 11 additions & 25 deletions packages/supabase_auth/lib/src/types/types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -227,14 +227,13 @@ class OAuthClient {
this.clientName,
this.clientSecret,
required this.clientType,
required this.tokenEndpointAuthenticationMethod,
required this.registrationType,
this.tokenEndpointAuthenticationMethod,
this.registrationType,
this.clientUri,
this.logoUri,
required this.redirectUris,
required this.grantTypes,
required this.responseTypes,
this.scope,
required this.createdAt,
required this.updatedAt,
});
Expand All @@ -246,10 +245,11 @@ class OAuthClient {
clientSecret: json['client_secret'] as String?,
clientType: OAuthClientType.fromValue(json['client_type'] as String),
tokenEndpointAuthenticationMethod:
json['token_endpoint_auth_method'] as String,
registrationType: OAuthClientRegistrationType.fromValue(
json['registration_type'] as String,
),
json['token_endpoint_auth_method'] as String?,
registrationType: switch (json['registration_type']) {
final String value => OAuthClientRegistrationType.fromValue(value),
_ => null,
},
clientUri: json['client_uri'] as String?,
logoUri: json['logo_uri'] as String?,
redirectUris: (json['redirect_uris'] as List?)?.cast() ?? const [],
Expand All @@ -270,7 +270,6 @@ class OAuthClient {
),
)
.toList(),
scope: json['scope'] as String?,
createdAt: parseIso8601(json, 'created_at'),
updatedAt: parseIso8601(json, 'updated_at'),
);
Expand All @@ -288,11 +287,11 @@ class OAuthClient {
/// Type of OAuth client
final OAuthClientType clientType;

/// Token endpoint authentication method
final String tokenEndpointAuthenticationMethod;
/// Token endpoint authentication method, if the server returned one
final String? tokenEndpointAuthenticationMethod;

/// Registration type of the client
final OAuthClientRegistrationType registrationType;
/// Registration type of the client, if the server returned one
final OAuthClientRegistrationType? registrationType;

/// URI of the OAuth client
final String? clientUri;
Expand All @@ -309,9 +308,6 @@ class OAuthClient {
/// Array of allowed response types
final List<OAuthClientResponseType> responseTypes;

/// Scope of the OAuth client
final String? scope;

/// Timestamp when the client was created
final DateTime createdAt;

Expand Down Expand Up @@ -341,7 +337,6 @@ class CreateOAuthClientOptions {
required this.redirectUris,
this.grantTypes,
this.responseTypes,
this.scope,
});

/// Human-readable name of the OAuth client
Expand All @@ -363,9 +358,6 @@ class CreateOAuthClientOptions {
/// Array of allowed response types (optional, defaults to code)
final List<OAuthClientResponseType>? responseTypes;

/// Scope of the OAuth client
final String? scope;

Map<String, dynamic> toJson() {
return {
'client_name': clientName,
Expand All @@ -382,7 +374,6 @@ class CreateOAuthClientOptions {
OAuthClientResponseType.unknown,
'responseTypes',
),
'scope': ?scope,
};
}
}
Expand All @@ -397,7 +388,6 @@ class UpdateOAuthClientOptions {
this.redirectUris,
this.grantTypes,
this.responseTypes,
this.scope,
});

/// Human-readable name of the OAuth client
Expand All @@ -418,9 +408,6 @@ class UpdateOAuthClientOptions {
/// Array of allowed response types
final List<OAuthClientResponseType>? responseTypes;

/// Scope of the OAuth client
final String? scope;

Map<String, dynamic> toJson() {
return {
'client_name': ?clientName,
Expand All @@ -437,7 +424,6 @@ class UpdateOAuthClientOptions {
OAuthClientResponseType.unknown,
'responseTypes',
),
'scope': ?scope,
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ void main() {
clientName: 'Test OAuth Client',
redirectUris: ['https://example.com/callback'],
clientUri: 'https://example.com',
scope: 'openid profile email',
);

final response = await client.admin.oauth.createClient(parameters);
Expand Down
7 changes: 2 additions & 5 deletions packages/supabase_auth/test/src/auth_oauth_api_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,6 @@ void main() {
clientName: 'Test OAuth Client',
redirectUris: ['http://127.0.0.1:3000/oauth/callback'],
responseTypes: [OAuthClientResponseType.code],
scope: 'email',
);
final oauthClient = await fixture.createOAuthClient(clientParameters);
final auth = await fixture.logIn(password: password, email: email1);
Expand All @@ -218,7 +217,7 @@ void main() {
expect(response, isA<OAuthAuthorizationDetailsResponse>());
final details = response as OAuthAuthorizationDetailsResponse;
expect(details.authorizationId, equals(authorizationId));
expect(details.scope, equals(clientParameters.scope));
expect(details.scope, equals('email'));
expect(details.redirectUri, equals(clientParameters.redirectUris.first));
expect(details.client.clientId, equals(oauthClient.clientId));
expect(details.client.clientName, equals(oauthClient.clientName));
Expand Down Expand Up @@ -320,7 +319,6 @@ void main() {
clientName: 'Test OAuth Client',
redirectUris: ['http://127.0.0.1:3000/oauth/callback'],
responseTypes: [OAuthClientResponseType.code],
scope: 'email',
);
final oauthClient = await fixture.createOAuthClient(clientParameters);
await fixture.logIn(password: password, email: email1);
Expand All @@ -341,7 +339,6 @@ void main() {
clientName: 'Test OAuth Client',
redirectUris: ['http://127.0.0.1:3000/oauth/callback'],
responseTypes: [OAuthClientResponseType.code],
scope: 'email',
);
final oauthClient = await fixture.createOAuthClient(clientParameters);
await fixture.logIn(password: password, email: email1);
Expand All @@ -364,7 +361,6 @@ void main() {
clientName: 'Test OAuth Client',
redirectUris: ['http://127.0.0.1:3000/oauth/callback'],
responseTypes: [OAuthClientResponseType.code],
scope: 'email',
);
final oauthClient = await fixture.createOAuthClient(clientParameters);
await fixture.logIn(password: password, email: email1);
Expand Down Expand Up @@ -441,6 +437,7 @@ class AuthOauthApiFixture {
'redirect_uri': client.redirectUris.first,
'code_challenge': 'pkqGrzhFuuBOcRoR4elYl2ki1EiR3KtBdtQsEZdv8rM',
'code_challenge_method': 'S256',
'scope': 'email',
},
),
)..followRedirects = false;
Expand Down
17 changes: 15 additions & 2 deletions packages/supabase_auth/test/src/types/oauth_client_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ void main() {
'redirect_uris': ['https://example.com/callback'],
'grant_types': ['authorization_code', 'refresh_token'],
'response_types': ['code'],
'scope': 'openid profile email',
'created_at': '2025-01-01T00:00:00.000Z',
'updated_at': '2025-01-02T03:04:05.000Z',
};
Expand All @@ -35,7 +34,6 @@ void main() {
OAuthClientGrantType.refreshToken,
]);
expect(client.responseTypes, [OAuthClientResponseType.code]);
expect(client.scope, 'openid profile email');
expect(client.createdAt, DateTime.parse('2025-01-01T00:00:00.000Z'));
expect(client.updatedAt, DateTime.parse('2025-01-02T03:04:05.000Z'));
});
Expand All @@ -53,6 +51,21 @@ void main() {
expect(client.logoUri, isNull);
});

test(
'fromJson parses a client without a token endpoint authentication '
'method or registration type',
() {
final client = OAuthClient.fromJson(
{...json}
..remove('token_endpoint_auth_method')
..remove('registration_type'),
);

expect(client.tokenEndpointAuthenticationMethod, isNull);
expect(client.registrationType, isNull);
},
);

test(
'fromJson parses a client without redirect URIs, grant types or '
'response types',
Expand Down
3 changes: 0 additions & 3 deletions sdk-compliance.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,6 @@ features:
- CreateOAuthClientOptions.logoUri
- CreateOAuthClientOptions.redirectUris
- CreateOAuthClientOptions.responseTypes
- CreateOAuthClientOptions.scope
- CreateOAuthClientOptions.toJson
auth.oauth_admin.get_client:
status: implemented
Expand All @@ -769,7 +768,6 @@ features:
- UpdateOAuthClientOptions.logoUri
- UpdateOAuthClientOptions.redirectUris
- UpdateOAuthClientOptions.responseTypes
- UpdateOAuthClientOptions.scope
- UpdateOAuthClientOptions.toJson
auth.oauth_admin.delete_client:
status: implemented
Expand Down Expand Up @@ -2894,7 +2892,6 @@ supporting_symbols:
- OAuthClient.redirectUris
- OAuthClient.registrationType
- OAuthClient.responseTypes
- OAuthClient.scope
- OAuthClient.tokenEndpointAuthenticationMethod
- OAuthClient.updatedAt
- OAuthClientGrantType
Expand Down
Loading