diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Discovery.cs b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Discovery.cs
index c28c4d69..2dfb14f7 100644
--- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Discovery.cs
+++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Discovery.cs
@@ -114,6 +114,13 @@ public static partial class OpenIddictClientWebIntegrationHandlers
context.Configuration.GrantTypesSupported.Add(GrantTypes.RefreshToken);
}
+ else if (context.Registration.ProviderType is ProviderTypes.Atlassian)
+ {
+ context.Configuration.GrantTypesSupported.Add(GrantTypes.AuthorizationCode);
+ context.Configuration.GrantTypesSupported.Add(GrantTypes.Implicit);
+ context.Configuration.GrantTypesSupported.Add(GrantTypes.RefreshToken);
+ }
+
else if (context.Registration.ProviderType is ProviderTypes.Auth0)
{
context.Configuration.GrantTypesSupported.Add(GrantTypes.AuthorizationCode);
@@ -214,16 +221,25 @@ public static partial class OpenIddictClientWebIntegrationHandlers
throw new ArgumentNullException(nameof(context));
}
- // While it is a recommended node, some providers don't include "scopes_supported" in their
- // configuration and thus are treated as OAuth 2.0-only providers by the OpenIddict client.
- // To avoid that, the "openid" scope is manually added to indicate OpenID Connect is supported.
+ // Atlassian includes the "openid" scope in its server configuration but doesn't currently allow
+ // requesting it. To prevent an error from being returned, OpenID Connect support is disabled.
+ if (context.Registration.ProviderType is ProviderTypes.Atlassian)
+ {
+ context.Configuration.ScopesSupported.Remove(Scopes.OpenId);
+ }
- if (context.Registration.ProviderType is ProviderTypes.DocuSign)
+ // DocuSign supports OpenID Connect but doesn't format the "openid" scope using the standard casing.
+ // To ensure DocuSign is not treated as an OAuth 2.0-only provider, the invalid "OpenId" scope is
+ // removed from the list and the "openid" value is added to indicate OpenID Connect is supported.
+ else if (context.Registration.ProviderType is ProviderTypes.DocuSign)
{
context.Configuration.ScopesSupported.Remove("OpenId");
context.Configuration.ScopesSupported.Add(Scopes.OpenId);
}
+ // While it is a recommended node, these providers don't include "scopes_supported" in their
+ // configuration and thus are treated as OAuth 2.0-only providers by the OpenIddict client.
+ // To avoid that, the "openid" scope is manually added to indicate OpenID Connect is supported.
else if (context.Registration.ProviderType is ProviderTypes.EpicGames or ProviderTypes.Xero)
{
context.Configuration.ScopesSupported.Add(Scopes.OpenId);
@@ -317,6 +333,14 @@ public static partial class OpenIddictClientWebIntegrationHandlers
ClientAuthenticationMethods.PrivateKeyJwt);
}
+ // Atlassian doesn't return a "revocation_endpoint_auth_methods_supported" node in its
+ // server configuration but only supports the "client_secret_post" authentication method.
+ else if (context.Registration.ProviderType is ProviderTypes.Atlassian)
+ {
+ context.Configuration.RevocationEndpointAuthMethodsSupported.Add(
+ ClientAuthenticationMethods.ClientSecretPost);
+ }
+
// Google doesn't properly implement the device authorization grant, doesn't support
// basic client authentication for the device authorization endpoint and returns
// a generic "invalid_request" error when using "client_secret_basic" instead of
@@ -366,10 +390,18 @@ public static partial class OpenIddictClientWebIntegrationHandlers
throw new ArgumentNullException(nameof(context));
}
+ // While Atlassian implements an OpenID Connect userinfo endpoint, using it requires
+ // requesting the "openid" scope, which isn't allowed yet. To work around this
+ // limitation, the userinfo endpoint is replaced by the generic /me endpoint URI.
+ if (context.Registration.ProviderType is ProviderTypes.Atlassian)
+ {
+ context.Configuration.UserinfoEndpoint = new Uri("https://api.atlassian.com/me", UriKind.Absolute);
+ }
+
// While Auth0 exposes an OpenID Connect-compliant logout endpoint, its address is not returned
// as part of the configuration document. To ensure RP-initiated logout is supported with Auth0,
// "end_session_endpoint" is manually computed using the issuer URI and added to the configuration.
- if (context.Registration.ProviderType is ProviderTypes.Auth0)
+ else if (context.Registration.ProviderType is ProviderTypes.Auth0)
{
context.Configuration.EndSessionEndpoint ??= OpenIddictHelpers.CreateAbsoluteUri(
context.Registration.Issuer, "oidc/logout");
diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs
index cbd7dcde..2422cc1e 100644
--- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs
+++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs
@@ -1241,6 +1241,9 @@ public static partial class OpenIddictClientWebIntegrationHandlers
ProviderTypes.ArcGisOnline or ProviderTypes.Trakt
=> (string?) context.UserinfoResponse?["username"],
+ // Atlassian returns the user identifier as a custom "account_id" node:
+ ProviderTypes.Atlassian => (string?) context.UserinfoResponse?["account_id"],
+
// These providers return the user identifier as a custom "id" node:
ProviderTypes.Basecamp or ProviderTypes.Box or ProviderTypes.Dailymotion or
ProviderTypes.Deezer or ProviderTypes.Discord or ProviderTypes.Disqus or
@@ -1578,6 +1581,18 @@ public static partial class OpenIddictClientWebIntegrationHandlers
context.Request["resource"] = settings.Resource;
}
+ // Atlassian requires sending an "audience" parameter (by default, "api.atlassian.com").
+ //
+ // The documentation also indicates the "prompt" parameter is required, but no error is
+ // returned if this parameter is not explicitly included in the authorization request.
+ else if (context.Registration.ProviderType is ProviderTypes.Atlassian)
+ {
+ var settings = context.Registration.GetAtlassianSettings();
+
+ context.Request.Audiences = [settings.Audience];
+ context.Request.Prompt = settings.Prompt;
+ }
+
// By default, Google doesn't return a refresh token but allows sending an "access_type"
// parameter to retrieve one (but it is only returned during the first authorization dance).
else if (context.Registration.ProviderType is ProviderTypes.Google)
diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml
index f2ebb082..0342765a 100644
--- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml
+++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml
@@ -80,6 +80,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+