ASP.NET Boilerplate Identity Server API Access Token - asp.net

I have successfully enabled Identity Server in ASP.Net Boilerplate and called it with a JS client as described by Identity Server docs http://docs.identityserver.io/en/latest/quickstarts/6_javascript_client.html . After logging on and getting an access token I cannot use this access token to access the API as I get 401.
I was unable to complete the final step as shown by Identity Server example. Because My projects throws an error on startup saying Identityserver already registered. I am assuming without this step my token is not generated correctly. However have not been able to work out how I should configure ABP to do the same thing.
services.AddAuthentication("Bearer").AddIdentityServerAuthentication(options =>
{
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.ApiName = "api1";
});
Any help appreciated :)
The exact error I get is 401 (Unauthorized). If i exchange the token for one generated through /api/TokenAuth/Authenticate the call works.
The Token generated through Identity server is "eyJhbGciOiJSUzI1NiIsImtpZCI6IjNhODAzNTUzNzNlNDVhMzRmNTI3MzJmM2ZjZWNjZTQ4IiwidHlwIjoiSldUIn0.eyJuYmYiOjE1NTExNTgyMjYsImV4cCI6MTU1MTE2MTgyNiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo2MjExNCIsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6NjIxMTQvcmVzb3VyY2VzIiwiY2xpZW50X2lkIjoianMiLCJzdWIiOiIxIiwiYXV0aF90aW1lIjoxNTUxMTU3MTcwLCJpZHAiOiJsb2NhbCIsInNjb3BlIjpbIm9wZW5pZCIsInByb2ZpbGUiXSwiYW1yIjpbInB3ZCJdfQ.LBJ5KfiOGjMSWlpsWbXLuGBnd0RHq07IWM7npYGPOBm38ENeZkzLErgwalTFH7acOOa8rHymfTFRBVQgO1sEy-nnxn-iPmjstKABu2Xe1o-qlsrU7K7mxN1FLKJWksWBty983TZ-WLrK9pXEHjN9LGeBFY-Qx_RPFOVu4gattjgNI05-J3a2dsnON_bJfvsXPL2ktUa_od-uqi9AXnWY_kJA-5xh1rjMP6pf740tMQJjhMGAIitQHbWiCfmvvPjX6bzBnMXFJpmiVT_hZsZ76zoQskLRQz8Zn-IfVhU9VM-8U7B6PKUaVFs4-VA2ia9VVwxuSs1gJoC9RwMqKYmX_g"
My Client is being registered with these properties.
var client = new Client
{
ClientId = "js",
ClientName = "JavaScript Client",
AllowedGrantTypes = GrantTypes.Code,
RequirePkce = true,
RequireClientSecret = false,
RequireConsent = true,
RedirectUris = { "http://localhost:5003/callback.html" },
PostLogoutRedirectUris = { "http://localhost:5003/index.html" },
AllowedCorsOrigins = { "http://localhost:5003" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"Apps"
}
};

Related

asp.net core OAuth access_token verification fails with "IDX10609: Decryption failed. No Keys tried: token: 'System.String'.:"

I have a single page application in Blazor WASM that connects to OAuth0 and receives JWT token when a user get authenticated via OAuth.
My OAuth application uses symmetric signing with HS256.
I am trying to authenticate the returned tokens using the "Client Secret" from my OAuth app but whereas the id_token gets verified, the access_token always fails with the error:
"IDX10609: Decryption failed. No Keys tried: token: 'System.String'.:"
To perform the verification I use the method found here.
My code looks like that:
var mySecret = "####";
var mySecurityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(mySecret));
var tokenHandler = new JwtSecurityTokenHandler();
try
{
IConfigurationManager<OpenIdConnectConfiguration> configurationManager =
new ConfigurationManager<OpenIdConnectConfiguration>(
$"{my_oauth_app_domain}.well-known/openid-configuration",
new OpenIdConnectConfigurationRetriever());
OpenIdConnectConfiguration openIdConfig = await configurationManager.GetConfigurationAsync(CancellationToken.None);
var keys = openIdConfig.SigningKeys;
tokenHandler.ValidateToken(token, new TokenValidationParameters
{
ValidateAudience = false,
ValidateIssuer = false,
ValidateActor = false,
ValidateLifetime = false,
ValidateTokenReplay = false,
ValidateIssuerSigningKey = true,
IssuerSigningKey = mySecurityKey,
IssuerSigningKeys = openIdConfig.SigningKeys,
}, out SecurityToken validatedToken);
}
catch(Exception ex)
{
Console.WriteLine($"{ex.Message}: {ex.StackTrace}");
return false;
}
return true;
What I find strange is that the access_token appears to be malformed. I know that it doesn't have to follow the JWT specs but puting it in jwt.io causes an error and fields like "kid" which I have seen in other tokens on the web, are missing.
Here is the token:
eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIiwiaXNzIjoiaHR0cHM6Ly9nYW50b25vcG91bG9zLmV1LmF1dGgwLmNvbS8ifQ..MQnqWWfe3mBCVeKQ.y8e77jf3VwJNRSoDWjB3v05WrT9IJPL_kdqhxlQFnfMOAyqQJOD1ttl1muYlCJJVwAskaAeBr4FgkcwjiL1s4eS9gcWK7yq-2PPbLkDzXtBjA4kgMGdGyMURl-F2jYBNwxbCuXyBKcxJVwzE4-aluYCAOZ8QaXzqKgmQJdpxIdBluVux7nK49uhvEJ5Pv7ueh7eGcm9AAmHm__TYKPPcpPutHNiuD6J8xoptHFLPjKakECE6ZXgD-xLNp4BHwe_DmW6UDPuZ_OD9G8D-hwayz8l--zZdICEnFywUzSWXFiVPUvn4DszDhzWbJsBNf3dnl2cnKel3EYsB.NvsUTcP9v_iicpQ5AkaC4w
Am I doing something wrong?
I found the problem here. In my request to OAuth0 I did not add the "audience" parameter. This led to an opaque token being issued.

Correlation failed on Identity Server 4

I have an Identity Server 4 configured with OpenIdConnect to Azure AD.
When user clicks on login button, IS4 redirects to Azure AD and on callback to IS4, it shows this error:
This is how I request token from postman:
Note that callback url is mobile application format.
This is my configuration:
services.AddAuthentication()
.AddCookie(options => new CookieAuthenticationOptions
{
ExpireTimeSpan = TimeSpan.FromHours(12),
SlidingExpiration = false,
Cookie = new CookieBuilder
{
Path = "",
Name = "MyCookie"
}
}).AddOpenIdConnect(options =>
{
options.ClientId = configuration["OpenIdConnect:ClientId"];
options.Authority = configuration["OpenIdConnect:Authority"];
options.SignedOutRedirectUri = configuration["OpenIdConnect:PostLogoutRedirectUri"];
options.CallbackPath = configuration["OpenIdConnect:CallbackPath"];
options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
options.Resource = configuration["OpenIdConnect:Resource"];
options.ClientSecret = configuration["OpenIdConnect:ClientSecret"];
options.SaveTokens = true;
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role"
};
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
});
And this are my parameters:
"OpenIdConnect": {
"ClientId": "xxxxxxxxxx",
"Authority": "https://login.microsoftonline.com/xxxxxxxxxx/",
"PostLogoutRedirectUri": "https://uri-of-my-identity-server.azurewebsites.net",
"CallbackPath": "/signin-oidc",
"ResponseType": "code id_token",
"Resource": "https://graph.microsoft.com/",
"ClientSecret": "my-secret"
},
Note: this error only occurs on Azure environment (not locally)
Note: on Xamarin application, when Azure returns to IS4 consent screen, it shows this message:
It could be that there is an issue with the networking between your client and Azure. A certain port has not been opened or a load balancer is in between.
When decryption fails, state is null, thus resulting in a Correlation failed: state not found error. In our case, decryption failed because different keys were used for encryption/decryption, a pretty common problem when deploying behind a load balancer.

identityserver4 with redux -oidc client requested access token - but client is not configured to receive access tokens via browser

My identityserver4 client looks like this:
new Client {
ClientId = "openIdConnectClient",
ClientName = "Example Implicit Client Application",
//AllowedGrantTypes = GrantTypes.Implicit,
AllowedGrantTypes = GrantTypes.ClientCredentials,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowOfflineAccess = true,
AllowAccessTokensViaBrowser = true,
AccessTokenLifetime = 30,
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
"role",
"customAPI.write"
},
RedirectUris = new List<string> {"http://localhost:8080/callback"},
PostLogoutRedirectUris = new List<string> {"https://localhost:44330"},
AllowedCorsOrigins = new List<string>
{
"http://127.0.0.1:8080",
"http://localhost:8080",
"*"
},
}
In react application, my userManager class looks like this:
import { createUserManager } from 'redux-oidc';
const userManagerConfig = {
client_id: 'openIdConnectClient',
redirect_uri: `${window.location.protocol}//${window.location.hostname}${window.location.port ? `:${window.location.port}` : ''}/callback`,
//response_type: 'code id_token token',
response_type: 'token id_token',
scope: 'openid profile email role',
authority: 'http://localhost:50604',
silent_redirect_uri: `${window.location.protocol}//${window.location.hostname}${window.location.port ? `:${window.location.port}` : ''}/silent_renew.html`,
automaticSilentRenew: true,
filterProtocolClaims: true,
loadUserInfo: true,
};
const userManager = createUserManager(userManagerConfig);
export default userManager;
The question is: when i try to call my identityserver4 from the redux-oidc example application. I'm getting the following error:
Client requested access token - but client is not configured to receive access tokens via browser
I hope you understood the question. Please someone help me with this. i have provided the link for this example application bellow.
Redux-oidc example app link
Your code contains two different grant types. The different Grant types in Identity server 4 have different requirements. Here is a bit of information to help you understand the different types you are using. It may also help you understand why you were having this problem.
GrantTypes.ClientCredentials
The Client credentials is the simplest grant type and is used for server to server communication - tokens are always requested on behalf of a client, not a user.
With this grant type you send a token request to the token endpoint, and get an access token back that represents the client. The client typically has to authenticate with the token endpoint using its client ID and secret.
new Client
{
ClientId = "client",
// no interactive user, use the clientid/secret for authentication
AllowedGrantTypes = GrantTypes.ClientCredentials,
// secret for authentication
ClientSecrets =
{
new Secret("secret".Sha256())
},
// scopes that client has access to
AllowedScopes = { "api1" }
}
GrantTypes.Implicit
The implicit grant type is optimized for browser-based applications. Either for user authentication-only (both server-side and JavaScript applications), or authentication and access token requests (JavaScript applications).
In the implicit flow, all tokens are transmitted via the browser, and advanced features like refresh tokens are thus not allowed. If you want to transmit access tokens via the browser channel, you also need to allow that explicitly on the client configuration:
Client.AllowAccessTokensViaBrowser = true;
new Client
{
ClientId = "mvc",
ClientName = "MVC Client",
AllowedGrantTypes = GrantTypes.Implicit,
// where to redirect to after login
RedirectUris = { "http://localhost:5002/signin-oidc" },
// where to redirect to after logout
PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile
},
AllowAccessTokensViaBrowser = true
}

Multiple Authentication Middlewares ASP.NET Core

I am relatively new to the concept of middlewares. I am aware that a middleware calls the next middleware when it completes.
I am trying to authenticate a request using either Google or my Identity Server. The user can login on my mobile app with google or a local account. However, I can't figure out how to use both authentication middlewares. If I pass the id_token for google, it passes on the first middleware (UseJwtBearerAuthentication) but fails on the second one (UseIdentityServerAuthentication). How can I make it so that it doesn't throw error when it actually passes on at least 1 authentication middleware? For example, if it passes on the first middleware, the second middleware is ignored?
app.UseJwtBearerAuthentication(new JwtBearerOptions()
{
Authority = "https://accounts.google.com",
Audience = "secret.apps.googleusercontent.com",
TokenValidationParameters = new TokenValidationParameters()
{
ValidateAudience = true,
ValidIssuer = "accounts.google.com"
},
RequireHttpsMetadata = false
});
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = "http://localhost:1000/",
RequireHttpsMetadata = false,
ScopeName = "MyApp.Api"
});
Normally, when an authentication middleware is failed(i don't mean throwing exception), this doesn't affect another successful authentication middleware. Probably your second middleware throws an exception(not a validation failure). First check error message and try to resolve it. If you can't, use AuthenticationFailed event to handle error. In this case your code should be something like below:
app.UseJwtBearerAuthentication(new JwtBearerOptions()
{
// ...
Events = new JwtBearerEvents()
{
OnAuthenticationFailed = async (context) =>
{
if (context.Exception is your exception)
{
context.SkipToNextMiddleware();
}
}
}
});
However, for your scenerio i wouldn't choose your way. I would use only identity server endpoint. For signing with google you can configure identity server like below:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme,
AutomaticAuthenticate = false,
AutomaticChallenge = false
});
app.UseGoogleAuthentication(new GoogleOptions
{
AuthenticationScheme = "Google",
SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme,
ClientId = "",
ClientSecret = ""
});
app.UseIdentityServer();
Edit
It seems AuthenticationFailed event couldn't be used for IdentityServer4.AccessTokenValidation. I am not sure but if you will use identity server for only jwt token, you can use UseJwtBearerAuthentication for validation.

OpenIDConnect AspNetCore Logout using id_token

Main issue is that I could not find a proper way to logout from identityServer4.
Detailed explanation:
Client side Web application startup.cs contains the following code
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = "Cookies",
AutomaticAuthenticate = true
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
AuthenticationScheme = "oidc",
SignInScheme = "Cookies",
Authority = "http://localhost:1941/",//local identityServer4
ClientId = "testsoft",
ClientSecret = "secret",
ResponseType = "code id_token token",
GetClaimsFromUserInfoEndpoint = true,
RequireHttpsMetadata = false,
Scope = { "openid", "profile", "email" },
TokenValidationParameters = new TokenValidationParameters()
{
NameClaimType = "name",
RoleClaimType = "role"
},
AutomaticAuthenticate = false,
AutomaticChallenge = true
});
IdentityServer4 running locally has the client added as below
new Client
{
ClientId = "testsoft",
ClientName = "testsoft",
ClientSecrets = new List<Secret>
{
new Secret("secret".Sha256())
},
ClientUri = "http://localhost:55383/",//clientside web application url
AllowedGrantTypes = GrantTypes.Hybrid,
AllowAccessTokensViaBrowser = true,
RedirectUris = new List<string>
{
"http://localhost:55383/signin-oidc"
},
RequireConsent = false,
AllowedScopes = new List<string>
{
StandardScopes.OpenId.Name,
StandardScopes.Profile.Name,
StandardScopes.Email.Name,
StandardScopes.Roles.Name,
StandardScopes.OfflineAccess.Name,
"api1", "api2",
},
},
I was able to login and display the claims on a Controller-View in MVC like this
[Authorize]
public IActionResult About()
{
return View((User as ClaimsPrincipal).Claims);
}
And the view displayed was like this. Note that there is no id_token
And I was able to logout using cookie as given below
public async Task<IActionResult> LogOut()
{
await HttpContext.Authentication.SignOutAsync("Cookies");
return Redirect("~/");
}
But the problem is I cannot find a way to logout from IdentityServer. The closer I came was to use
/connect/endsession?id_token_hint=...&post_logout_redirect_uri=https://myapp.com
But I could not find a way to get raw id_token in code. In the About() method given above I am only getting the claims (which I think is the decrypted contents of id_token) and in those claims list there is no id_token to be seen. But somehow managed to get the id_token from fiddler at this url http://localhost:55383/signin-oidc and then the logout at identityServer triggered(with the help of the url given above).
I have the following questions:
How to get id_token in code? (instead of manual copy from fiddler)
Is there a better way to logout? Or is there an AspnetCore/Oidc framework method to logout (which in turn call the correct server api with correct parameters) ?
I was able to logout and login several times but the id_token was seen the same on fiddler. eg: Bob user, Alice user both had the same id_token. Cookie was cleared and each time different user was displayed on the view still the id_token was same. shouldn't the id_token be different for each login/user?
Signout url worked even when I gave a random string as id_token. Does this mean that IdentityServer4 logout functionality do not work based on id_token?
For logging out, did you try-
HttpContext.Authentication.SignOutAsync("oidc");
in your client's Logout Action?

Resources