I’m using Okta as an external IDP with IdentityServer 3 (legacy software, I know). I’ve used IdentityServer 3 with other OIDC clients and they’ve all worked correctly. Okta works correctly when the user is logging in from our website, using the hybrid flow with an Okta OIDC client. When logging in from the mobile app (hitting identityserver within an in-app browser, then hitting external Okta login) from a similarly set up client, the login fails.
After some investigation, Okta correctly passes back the code/tokens but it seems that the signin id may not be getting passed back from Okta when hitting /core/callback endpoint. This causes a log from IdentityServer (or .NET itself) saying: “no signin id passed” before redirecting to an error page which says “There is an error determining which application you are signing into. Return to the application and try again.” I’m reasonably sure this is an issue with Okta because we have used the exact same configuration with other OIDC external IDPs before with mobile apps and it’s worked flawlessly.
Here’s my configuration code, if it’s helpful:
in Clients.cs of IdentityServer:
new Client
{
Enabled = true,
ClientName = "REDACTEDCLIENTNAME",
ClientId = "REDACTEDCLIENTID",
Flow = Flows.HybridWithProofKey,
AllowedScopes = new List<string>
{
Constants.StandardScopes.OpenId,
Constants.StandardScopes.Profile,
Constants.StandardScopes.Email,
Constants.StandardScopes.Roles,
Constants.StandardScopes.OfflineAccess,
"REDACTEDService",
"REDACTEDClientService"
},
IdentityTokenLifetime = FourteenDays,
AccessTokenLifetime = FourteenDays,
ClientUri = "https://REDACTED.com",
RequireConsent = false,
AllowRememberConsent = true,
RedirectUris = new List<string>
{
"REDACTEDAPP://",
"https://REDACTEDIDENTITYSERVER.com/core/", // Testing, this wasn't needed with other mobile clients
"https://REDACTEDIDENTITYSERVER.com/core/callback", // Testing, this wasn't needed with other mobile clients
},
PostLogoutRedirectUris = new List<string>
{
"REDACTEDAPP://"
},
ClientSecrets = new List<Secret> {
new Secret("REDACTEDSECRET".Sha256())
}
},
And in Startup.cs of IdentityServer:
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions("TestOkta")
{
Authority = "https://dev-REDACTED.okta.com/oauth2/default",
ClientId = "REDACTEDCLIENTID,
ClientSecret = "REDACTEDSECRET",
RedirectUri = "https://REDACTEDIdentityServer/core",
ResponseType = "code id_token",
Scope = "openid profile email offline_access",
Caption = "TestOkta",
SignInAsAuthenticationType = "Cookies",
UseTokenLifetime = false,
TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
NameClaimType = "name",
ValidIssuer = "https://dev-REDACTED.okta.com/oauth2/default",
ValidateIssuer = true
},
MetadataAddress = "https://dev-REDACTED.okta.com/oauth2/default/.well-known/openid-configuration",
Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = n =>
{
// Get id hint from owin context and add to protocol message
// if signing out, add the id_token_hint
if (n.ProtocolMessage.RequestType == Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectRequestType.Logout)
{
var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token");
if (idTokenHint != null)
{
n.ProtocolMessage.IdTokenHint = idTokenHint.Value;
}
// Okta does not support post logout redirect uri with dynamic id params, so remove it
if (n.ProtocolMessage.IssuerAddress.Contains("okta"))
{
n.ProtocolMessage.PostLogoutRedirectUri = null;
}
}
return Task.FromResult(0);
},
SecurityTokenValidated = (context) =>
{
// Add ID token as new claim to the identity, to be used on logout for OIDC id_token_hint
var token = context.ProtocolMessage.IdToken;
context.AuthenticationTicket.Identity.AddClaim(new System.Security.Claims.Claim("id_token", token));
return Task.FromResult(0);
}
}
});