diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs
index 3e83a74e..c1f6c4a6 100644
--- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs
+++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs
@@ -28,6 +28,7 @@ public static partial class OpenIddictClientWebIntegrationHandlers
OverrideTokenEndpointClientAuthenticationMethod.Descriptor,
OverrideTokenEndpoint.Descriptor,
AttachNonStandardClientAssertionClaims.Descriptor,
+ OverrideScopes.Descriptor,
AttachAdditionalTokenRequestParameters.Descriptor,
AttachTokenRequestNonStandardClientCredentials.Descriptor,
AdjustRedirectUriInTokenRequest.Descriptor,
@@ -603,6 +604,40 @@ public static partial class OpenIddictClientWebIntegrationHandlers
}
}
+ ///
+ /// Contains the logic responsible for overriding the scopes for the providers that require it.
+ ///
+ public sealed class OverrideScopes : IOpenIddictClientHandler
+ {
+ ///
+ /// Gets the default descriptor definition assigned to this handler.
+ ///
+ public static OpenIddictClientHandlerDescriptor Descriptor { get; }
+ = OpenIddictClientHandlerDescriptor.CreateBuilder()
+ .AddFilter()
+ .UseSingletonHandler()
+ .SetOrder(AttachTokenRequestParameters.Descriptor.Order - 500)
+ .SetType(OpenIddictClientHandlerType.BuiltIn)
+ .Build();
+
+ ///
+ public ValueTask HandleAsync(ProcessAuthenticationContext context)
+ {
+ ArgumentNullException.ThrowIfNull(context);
+
+ // osu! requires at least one scope to be set for client credentials grant, as tokens without
+ // scopes are not valid. If no scope is explicitly specified, use the default value "public".
+ if (context.GrantType is GrantTypes.ClientCredentials &&
+ context.Registration.ProviderType is ProviderTypes.Osu &&
+ context.Scopes.Count is 0)
+ {
+ context.Scopes.Add("public");
+ }
+
+ return ValueTask.CompletedTask;
+ }
+ }
+
///
/// Contains the logic responsible for attaching additional parameters
/// to the token request for the providers that require it.
@@ -887,7 +922,7 @@ public static partial class OpenIddictClientWebIntegrationHandlers
// Note: these providers don't have a static userinfo endpoint attached to their configuration
// so OpenIddict doesn't, by default, send a userinfo request. Since a dynamic endpoint is later
// computed and attached to the context, the default value MUST be overridden to send a request.
- ProviderTypes.Dailymotion or ProviderTypes.HubSpot or
+ ProviderTypes.Dailymotion or ProviderTypes.HubSpot or ProviderTypes.Osu or
ProviderTypes.SuperOffice or ProviderTypes.Zoho
when context.GrantType is GrantTypes.AuthorizationCode or GrantTypes.DeviceCode or
GrantTypes.Implicit or GrantTypes.Password or
@@ -1017,6 +1052,16 @@ public static partial class OpenIddictClientWebIntegrationHandlers
left : new Uri("https://api.hubapi.com/oauth/v1/access-tokens", UriKind.Absolute),
right: new Uri(token, UriKind.Relative)),
+ // osu! supports specifying a game mode when querying the userinfo endpoint.
+ ProviderTypes.Osu => context.Properties.GetValueOrDefault(Osu.Properties.GameMode) switch
+ {
+ { Length: > 0 } mode => OpenIddictHelpers.CreateAbsoluteUri(
+ left : new Uri("https://osu.ppy.sh/api/v2/me", UriKind.Absolute),
+ right: new Uri(mode, UriKind.Relative)),
+
+ _ => new Uri("https://osu.ppy.sh/api/v2/me", UriKind.Absolute)
+ },
+
// SuperOffice doesn't expose a static OpenID Connect userinfo endpoint but offers an API whose
// absolute URI needs to be computed based on a special claim returned in the identity token.
ProviderTypes.SuperOffice when
@@ -1387,7 +1432,7 @@ public static partial class OpenIddictClientWebIntegrationHandlers
ProviderTypes.ArcGisOnline or ProviderTypes.Dailymotion or ProviderTypes.DeviantArt or
ProviderTypes.Discord or ProviderTypes.Disqus or ProviderTypes.Kook or
ProviderTypes.Lichess or ProviderTypes.Mastodon or ProviderTypes.Mixcloud or
- ProviderTypes.Trakt or ProviderTypes.WordPress
+ ProviderTypes.Osu or ProviderTypes.Trakt or ProviderTypes.WordPress
=> (string?) context.UserInfoResponse?["username"],
// These providers don't return a username so one is created using the "first_name" and "last_name" nodes:
@@ -1482,17 +1527,18 @@ public static partial class OpenIddictClientWebIntegrationHandlers
ProviderTypes.Atlassian => (string?) context.UserInfoResponse?["account_id"],
// These providers return the user identifier as a custom "id" node:
- ProviderTypes.Airtable or ProviderTypes.Basecamp or ProviderTypes.Box or
- ProviderTypes.Dailymotion or ProviderTypes.Deezer or ProviderTypes.Discord or
- ProviderTypes.Disqus or ProviderTypes.Facebook or ProviderTypes.Figma or
- ProviderTypes.Genesys or ProviderTypes.Gitee or ProviderTypes.GitHub or
- ProviderTypes.Harvest or ProviderTypes.Kook or ProviderTypes.Kroger or
- ProviderTypes.Lichess or ProviderTypes.Linear or ProviderTypes.Mastodon or
- ProviderTypes.Meetup or ProviderTypes.Miro or ProviderTypes.Nextcloud or
- ProviderTypes.Patreon or ProviderTypes.Pipedrive or ProviderTypes.Reddit or
- ProviderTypes.Smartsheet or ProviderTypes.Spotify or ProviderTypes.SubscribeStar or
- ProviderTypes.Todoist or ProviderTypes.Twitter or ProviderTypes.Webflow or
- ProviderTypes.Weibo or ProviderTypes.Yandex or ProviderTypes.Zoom
+ ProviderTypes.Airtable or ProviderTypes.Basecamp or ProviderTypes.Box or
+ ProviderTypes.Dailymotion or ProviderTypes.Deezer or ProviderTypes.Discord or
+ ProviderTypes.Disqus or ProviderTypes.Facebook or ProviderTypes.Figma or
+ ProviderTypes.Genesys or ProviderTypes.Gitee or ProviderTypes.GitHub or
+ ProviderTypes.Harvest or ProviderTypes.Kook or ProviderTypes.Kroger or
+ ProviderTypes.Lichess or ProviderTypes.Linear or ProviderTypes.Mastodon or
+ ProviderTypes.Meetup or ProviderTypes.Miro or ProviderTypes.Nextcloud or
+ ProviderTypes.Osu or ProviderTypes.Patreon or ProviderTypes.Pipedrive or
+ ProviderTypes.Reddit or ProviderTypes.Smartsheet or ProviderTypes.Spotify or
+ ProviderTypes.SubscribeStar or ProviderTypes.Todoist or ProviderTypes.Twitter or
+ ProviderTypes.Webflow or ProviderTypes.Weibo or ProviderTypes.Yandex or
+ ProviderTypes.Zoom
=> (string?) context.UserInfoResponse?["id"],
// Bitbucket returns the user identifier as a custom "uuid" node:
diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml
index 2a44dd9c..c2ae4645 100644
--- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml
+++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml
@@ -1747,6 +1747,39 @@
ConfigurationEndpoint="https://api.orange.com/openidconnect/fr/v1/.well-known/openid-configuration" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+