Blazor Webassembly : Identityserver4 NullReference AddQueryString - identityserver4

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.

Related

IdentityServer4: "code challenge required" under hybrid flow

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:

ASP.NET Zero solution for both identityserver and client

Before getting into the issue, let me tell you what I am trying to achieve.
I need to implement sort of SSO in all of my applications. For which I want to use ASP.NET Zero solutions as
SSO Provider as well as Clients.
Is it possible or am I overthinking?
I am using ASP.NET Zero template: ASP.NET Core - MVC & jQuery
I am very new to IdentityServer and OpenId so please excuse for my silly mistakes if I have made.
In one of ABP project, I have added a static client to IdentityServer AppSettings like below.
First project's AppSettings - Hosted application
{
"ClientId": "localhost",
"ClientName": "MVC Client Demo",
"AllowedGrantTypes": [
"implicit"
],
"RequireConsent": "true",
"ClientSecrets": [
{
"Value": "test"
}
],
"RedirectUris": [
"https://localhost:44302/signin-oidc"
],
"PostLogoutRedirectUris": [
"https://localhost:44302/Account/Login"
],
"AllowedScopes": [
"openid",
"profile",
"email",
"phone",
"default-api"
],
"AllowOfflineAccess": "true"
}
Now from my second ABP project (localhost), I am trying to enable OpenId to authenticated through above server.
Second project's AppSettings - Running on localhost
"OpenId": {
"IsEnabled": "true",
"Authority": "https://[applicationname].azurewebsites.net/",
"ClientId": "localhost",
"ClientSecret": "test",
"ValidateIssuer": "true",
"ClaimsMapping": [
{
"claim": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier",
"key": "http://schemas.microsoft.com/identity/claims/objectidentifier"
}
]
}
However I am not getting any error, in logs I can see there is a message that says:
AuthenticationScheme: Identity.External signed in.
And a cookie is being created with key "Identity.External" but login-is not happening successfully.
Inside AccountController below line returns null and that resulting into unsuccessful login.
**var externalLoginInfo = await _signInManager.GetExternalLoginInfoAsync();**
if (externalLoginInfo == null)
{
Logger.Warn("Could not get information from external login.");
return RedirectToAction(nameof(Login));
}
Try adding
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Add("sub", ClaimTypes.NameIdentifier);
before services.AddAuthentication()
This will map sub claim to NameIdentifier claim so GetExternalLoginInfoAsync will not return null.

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.

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