IdentityServer4: "code challenge required" under hybrid flow - identityserver4

I have a MVC client accessing a web API protected by IDS4 server. It works perfectly when I use ResponseType = "code". But when I change it to ResponseType = "code id_token" (hybrid flow), I start to get this "Sorry, there was an error : invalid_request code challenge required" error.
Here is my client configuration on the IDS4 side:
new Client
{
ClientId = "mvc",
ClientSecrets = { new Secret("secret".Sha256()) },
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
RequirePkce = false,
//AllowedGrantTypes = GrantTypes.Code,
RedirectUris = { "https://localhost:6009/signin-oidc" },
PostLogoutRedirectUris = { "https://localhost:6009/signout-callback-oidc" },
AllowOfflineAccess = true,
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
"roles",
"MyAPI"
}
And here is the configuration on the MVC Client side. Note that it is the 'options.ResponseType = "code id_token"' that made the error occur. It will not error if I use "code" instead.
.AddOpenIdConnect("oidc", options =>
{
options.Authority = "https://localhost:6005";
options.ClientId = "mvc";
options.ClientSecret = "secret";
//options.ResponseType = "code";
options.ResponseType = "code id_token";
options.SaveTokens = true;
options.Scope.Add("profile");
options.Scope.Add("email");
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("roles");
options.ClaimActions.MapJsonKey("role", "role", "role");
options.TokenValidationParameters.RoleClaimType = "role";
options.Scope.Add("MyAPI");
options.Scope.Add("offline_access");
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name"
};
});
And here is what's in the log:
[2021-03-12T15:25:51.014Z] [4] [Error] Request validation failed
[2021-03-12T15:25:51.013Z] [4] [Error] code_challenge is missing
{
"ClientId": "mvc",
"RedirectUri": "https://localhost:6009/signin-oidc",
"AllowedRedirectUris": [
"https://localhost:6009/signin-oidc"
],
"SubjectId": "anonymous",
"ResponseType": "code id_token",
"ResponseMode": "fragment",
"GrantType": "hybrid",
"RequestedScopes": "",
"State": "CfDJ....8oxh4",
"PromptMode": "",
"Raw": {
"client_id": "mvc",
"redirect_uri": "https://localhost:6009/signin-oidc",
"response_type": "code id_token",
"scope": "openid profile email roles MyAPIoffline_access",
"response_mode": "form_post",
"nonce": "637511...lOGNh",
"state": "CfDJ8...8oxh4",
"x-client-SKU": "ID_NETSTANDARD2_0",
"x-client-ver": "5.5.0.0"
}
}
By the way, I am testing hybrid flow because I am trying to make the Windows authentication working under IDS4. My local login works but not Windows. Some resource says that hybrid flow is needed for that. Can somebody tell me what I am doing wrong here?

It could be that IdentityServer always requires PKCE and that's why it complains when you don't provide the necessary challenge.
Dominic writes here:
Code flow without PKCE is vulnerable to code injection attacks. That's why we do not expose it.
Adding the code challenge/verifier in a client is not that complicated.
Here's how I present OAuth 2.1 in my training classes:

Related

Blazor Webassembly : Identityserver4 NullReference AddQueryString

I am trying to get a Blazor WebAssembly project working with an existing ASP.NET Core app that uses IdentityServer4(3.1).
When the Blazor app calls the following URL
http://192.168.0.151:5002/connect/authorize?client_id=myWebClient&redirect_uri=http%3A%2F%2F192.168.0.151%3A44387%2Fauthentication%2Flogin-callback&response_type=code&scope=openid%20profile%20openid%20profile&state=5d01e8dfd9ea4c3dbc207839f147bd37&code_challenge=-cXZb1q16t4eTMBaT8-lbeFGRr7V9YIbkl072JPhpQ4&code_challenge_method=S256&response_mode=query
I see from the logs that there is a null reference in AddQueryString.
Not sure how to resolve this.
IdentityServer4 is configured as follows
ClientId = "myWebClient",
ClientName = "My Web Client",
AllowedGrantTypes = GrantTypes.Code,
RequireClientSecret = false,
RequirePkce = true,
AllowedScopes =
{
"openid",
"profile",
},
AllowedCorsOrigins = { "http://192.168.0.151:44387" },
RedirectUris = { "http://192.168.0.151:44387/authentication/login-callback" },
PostLogoutRedirectUris = { "http://192.168.0.151:44387/" },
Enabled = true
And Blazor as follows
"Authority": "http://192.168.0.151:5002/",
"ClientId": "myWebClient",
"PostLogoutRedirectUri": "http://192.168.0.151:44387/",
"RedirectUri": "http://192.168.0.151:44387/authentication/login-callback",
"ResponseType": "code",
"DefaultScopes": [ "openid", "profile" ]
With IdentityServer initialised as follows
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryPersistedGrants()
.AddInMemoryIdentityResources(IdentityServerConfig.GetIdentityResources())
.AddInMemoryApiResources(IdentityServerConfig.GetApiResources())
.AddInMemoryClients(IdentityServerConfig.GetClients())
.AddAspNetIdentity<User>().AddProfileService<CustomProfileService>();
Any help greatly appreciated.
I think what I am missing are server pages that handle
login/logout etc. I assumed incorrectly these were present in Blazor webassembly.

Wrong audience for id_token in IdentityServer4

I'm trying to setup a solution with the following:
- IdentityServer4 instance
- React / js client
- ASP.NET Core API (protected by IdentityServer)
Since I want to use roles and claims, I would like to use a reference token (id_token) and have the API verify the claims against the IdentityServer.
Configuration for the IdentityServer instance:
"IdentityServer": {
"IdentityResources": [
"openid",
"email",
"phone",
"profile"
],
"ApiResources": [
{
"Name": "b2a6f5a1-9317-4b2f-bb02-c2f7cd70ce9a",
"DisplayName": "My API",
"ApiSecrets": [ { "Value": "<BASE 64 ENCODED SHA256 HASH OF SECRET>" } ]
}
],
"Clients": [
{
"Enabled": true,
"ClientId": "976d5079-f190-41a2-a6f6-be92470bacc0",
"ClientName": "My JS client",
"ClientUri": "http://localhost:3000",
"LogoUri": "logo.png",
"RequireClientSecret": false,
"AllowAccessTokensViaBrowser": true,
"RequireConsent": false,
"ClientClaimsPrefix": null,
"AccessTokenType": "reference",
"AllowedGrantTypes": [ "implicit" ],
"RedirectUris": [ "http://localhost:3000/authentication/login-callback" ],
"PostLogoutRedirectUris": [ "http://localhost:3000/authentication/logout-callback" ],
"AllowedCorsOrigins": [ "http://localhost:3000" ],
"AllowedScopes": [ "openid", "email", "phone", "profile", "b2a6f5a1-9317-4b2f-bb02-c2f7cd70ce9a" ]
}
]
}
Configuration for the (protected) API:
"Identity": {
"Authority": "https://localhost:44311",
"ApiName": "b2a6f5a1-9317-4b2f-bb02-c2f7cd70ce9a",
"ApiSecret": "<UNHASHED SECRET>"
}
Startup.cs for the API:
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = IdentityServerAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = IdentityServerAuthenticationDefaults.AuthenticationScheme;
})
.AddIdentityServerAuthentication(options =>
{
options.Authority = Configuration.GetValue<string>("Identity:Authority");
options.ApiName = Configuration.GetValue<string>("Identity:ApiName");
options.ApiSecret = Configuration.GetValue<string>("Identity:ApiSecret");
});
I query IdentityServer for an id_token with the following parameters:
export const Settings: any = {
authority: "https://localhost:44311",
post_logout_redirect_uri: "http://localhost:3000/authentication/logout-callback",
redirect_uri: "http://localhost:3000/authentication/login-callback",
response_type: "id_token",
scope: "openid email profile phone b2a6f5a1-9317-4b2f-bb02-c2f7cd70ce9a"
};
I get the following error: Requests for id_token response type only must not include resource scopes.
If I change the scope to:
export const Settings: any = {
// ...
scope: "openid email profile phone" // removed (protected) api resource
};
it works and I get an id_token like this:
{
"nbf": 1573798909,
"exp": 1573799209,
"iss": "https://localhost:44311",
"aud": "976d5079-f190-41a2-a6f6-be92470bacc0",
"nonce": "d768a177af684324b30ba73116a0ae79",
"iat": 1573798909,
"s_hash": "HbWErYNKpgsiOIO82IiReA",
"sid": "vVWVhLnVLiCMdLSBWnVUQA",
"sub": "90f84a26-f756-4923-9d26-6104eef031ac",
"auth_time": 1573798909,
"idp": "local",
"preferred_username": "noreply",
"name": "noreply",
"email": "noreply#example.com",
"email_verified": false,
"amr": [
"pwd"
]
}
Note that the audience is 976d5079-f190-41a2-a6f6-be92470bacc0, which is the js client.
When I use this token on the protected API, it says:
Bearer error="invalid_token", error_description="The audience '976d5079-f190-41a2-a6f6-be92470bacc0' is invalid"
which is not that strange since the API has the id b2a6f5a1-9317-4b2f-bb02-c2f7cd70ce9a.
So my question is: Where am I wrong? How do I get the token for the correct audience?
The ID token will be validated by your client app (React/js) app to get user claims , so the audience is your client app's client ID . A token passe to your web api should be validated by web api , so the audience is web api's name .
The ID token contains information about an End-User which is not used to access protected resource , while Access token allows access to certain defined server resources .You can set response_type to id_token token , and add api name/scope to scope configuration . With implicit flow , after authentication , client will get one ID token and one Access token , you can now use access token to access the protected web api .

How to log out of Google external provider?

Right now I can log out of Identity Server. But when logging back in, I can just select my email address - without having to reenter my password - to log in though Google to access my app.
I want to have to reenter my password (because the device is shared between multiple users). I followed the documentation, but I must be missing something.
(I am using a MVC client to test things out)
Here is the client's configuration:
{
"Enabled": true,
"EnableLocalLogin": false,
"ClientId": "backOffice.mvc",
"ClientName": "BackOffice client",
"ClientSecrets": [
{
"Value": "xxx"
}
],
"AllowedGrantTypes": [
"hybrid"
],
"AllowedScopes": [
"openid",
"offline_access",
"profile"
],
"RedirectUris": [
"http://localhost:5098/signin-oidc"
],
"PostLogoutRedirectUris": [
"http://localhost:5098/"
],
"RequireConsent": false,
"AllowOfflineAccess": true
}
And the provider settings:
.AddOpenIdConnect("Google", "Google", options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.ForwardSignOut = IdentityServerConstants.DefaultCookieAuthenticationScheme;
options.Authority = "https://accounts.google.com/";
options.ClientId = Configuration["GoogleClientId"];
options.CallbackPath = "/signin-google";
options.Scope.Add("email");
})
Thanks a lot for any help! And please let me know if you need more informations :)
Unfortunately Google does not advertise an end_session endpoint via https://accounts.google.com/.well-known/openid-configuration so front-channel sign out is not an option.
However you may be able to provide an additional prompt=login parameter in the authorize endpoint request in an attempt to force interactive authentication. You can enforce this in your client by checking that the auth_time claim is suitably recent.

Email Scope missing in Identity Server 4 even when added to Client

I am using Identity Server 4 with an Angular client.
In both sides I am adding the same scopes:
'openid profile offline_access email api'
However I get an error:
Sorry, there was an error : invalid_scope
Invalid scope
I checked the Well-Known OpenID configuration and I see:
"scopes_supported": [
"openid",
"profile",
"api",
"offline_access"
]
So the email scope does not show?
Might this be the problem? Why does this happen?
My Identity Server 4 Client definition is:
new Client {
ClientId = "spa",
ClientSecrets = { new Secret("secret".Sha256()) },
AllowedGrantTypes = GrantTypes.Code,
RequirePkce = true,
RequireClientSecret = false,
AllowAccessTokensViaBrowser = true,
AllowOfflineAccess = true,
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.OfflineAccess,
"api"
},
RedirectUris = new List<String> {
"https://localhost:5001",
"https://localhost:5001/index.html",
"https://localhost:5001/silent-refresh.html"
},
PostLogoutRedirectUris = new List<String> {
"https://localhost:5001/"
},
AllowedCorsOrigins = new List<String> {
"https://localhost:5001"
}
}
You need to add an IdentityResource:
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResources.Email()
};
}
Then in startup.cs add this:
var identityServerBuilder = services.AddIdentityServer(...)
.AddInMemoryIdentityResources(GetIdentityResources())

Can't login with WinForms app to IdentityServer4 using IdentityModel.OidcClient2

I get an invalid grant when I try to login from my WinForms app to IS4.
This is the server log:
fail: IdentityServer4.Validation.TokenRequestValidator[0]
Unexpected code_verifier: 12a783b32873a5b4ae0eb7113a067cd978d3d345a8cb29cc0a1a6df131c5839a
fail: IdentityServer4.Validation.TokenRequestValidator[0]
{
"ClientId": "las",
"ClientName": "LAS.NET Client",
"GrantType": "authorization_code",
"AuthorizationCode": "e301575cc20f47acf7c15178310f776642a7a30cf2b6a05f54702097b1645b7a",
"Raw": {
"grant_type": "authorization_code",
"code": "e301575cc20f47acf7c15178310f776642a7a30cf2b6a05f54702097b1645b7a",
"redirect_uri": "http://localhost/winforms.client",
"code_verifier": "12a783b32873a5b4ae0eb7113a067cd978d3d345a8cb29cc0a1a6df131c5839a",
"client_id": "las",
"client_secret": "secret"
}
}
The LoginResult.Error says "invalid_grant".
This is the client setup:
new Client
{
ClientId = "las",
ClientName = "LAS.NET Client",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.Phone,
"api1"
},
RedirectUris = { "http://localhost/winforms.client" },
AllowOfflineAccess = true,
RequireConsent = false
},
and this is how I initialize my winform app:
var options = new OidcClientOptions
{
Authority = "http://localhost:5000",
ClientId = "las",
ClientSecret = "secret",
RedirectUri = "http://localhost/winforms.client",
Scope = "openid profile api1 offline_access",
Browser = new WinFormsEmbeddedBrowser(),
Flow = OidcClientOptions.AuthenticationFlow.Hybrid
};
_oidcClient = new OidcClient(options);
How can I fix this issue?
Your WinForms client is telling IdentityServer that it wants to do PKCE however the client does not look like it requires PKCE. In your client configuration/setup add RequirePkce = true.

Resources