Can someone please explain what the difference is between the two? I understand Client Secrets, but Scope Secrets are still not clear... as well as why a Scope Secret even needs to exist?
While I found the documentation helpful in some ways, I did not find it helpful in explaining the difference between the two.
With thanks to Scott Brady I was able to answer my question. Here is what I found out...
Client Secrets
As Scott Brady said, Client Secrets are used when your client application calls the token end point. Your client application must have a valid Client Id and Client Secret to call the token end point.
Scope Secrets
But what if your resource server needs to call IdentityServer? This happens when the introspection endpoint is called by the resource server. This occurs when your application uses reference tokens or uses server side validation of JWTs. So suppose your authenticated client makes a call to a protected end point on your resource server. The resource server must then validate that JWT bearer token with IdentityServer. However, the request sent from your resource server to IdentityServer must be a protected call. That is, the resource server must present a JWT bearer token to IdentityServer in order to use the introspection endpoint. The resource server can't use the token that it is trying to validate since that belongs to a client (the audience is not for the resource server, it's for the client application). Additionally, the resource server is not sure if the bearer token is even valid. It wouldn't make sense for the resource server to use the bearer token in question to authenticate the request that validates said bearer token. So now there is a problem... How can the resource server send authenticated requests to IdentityServer?
This is where Scope Secrets come in. The way IdentityServer has solved this problem is to allow you to create a Scope that contains a Scope Secret. This Scope is added in your resource server authentication options. For example:
app.UseIdentityServerBearerTokenAuthentication(
new IdentityServerBearerTokenAuthenticationOptions
{
Authority = "http://localhost:5000",
ClientId = "api", //The Scope name
ClientSecret = "api-secret", //This is the non hashed/encrypted Scope Secret
RequiredScopes = new[] { "api" } //Must add the Scope name here
});
By making this Scope required we can ensure that any authenticated client app will have this Scope. Then in IdentityServer you would add the Scope like so:
new Scope
{
Name = "api",
DisplayName = "Scope DisplayName",
Description = "This will grant you access to the API",
//The secret here must be Sha256-ed in order for the /introspection end point to work.
//If the API's IdentityServerBearerTokenAuthenticationOptions field is set as so ValidationMode = ValidationMode.ValidationEndpoint,
//then the API will call the /introspection end point to validate the token on each request (instead of ValidationModel.ValidationLocal.
//The ClientSecret must be the NON Sha256-ed string (for example Api = "api-secret" then scope secret must = "api-secret".Sha256())
//for the token to be validated. There must be a Scope that has the same name as the ClientId field in IdentityServerBearerTokenAuthenticationOptions.
//This is an API authenticates with IdentityServer
ScopeSecrets = new List<Secret>
{
new Secret("api-secret".Sha256())
},
Type = ScopeType.Resource
}
So when the resource server makes the call to the introspection end point it will simply use the Scope Secret as the Client Secret and the Scope name as the Client Id.
Client Secrets are used to authorize access to the token endpoint. This endpoint uses a client id and a client secret, and allows you to request access tokens.
Scope Secrets are used to authorize access to the introspection endpoint. This endpoint uses a scope id and a scope secret, as only scopes that are contained in an access token are allowed to introspect it.
Related
I'm working on an MVC web project which is using IdentityServer4 to authenticate users, the web app then uses an access token provided to a user by IdentityServer (authorization code flow) to call an API. The IdentityServer has been configured to use Azure AD as an external identity provider, which is the primary mechanism for users to login. That's all working great.
Once authenticated, I need to query the web app's database to determine:
If the user account is authorised to login
Retrieve claims about the user specific to the application
The IdentityServer docs (http://docs.identityserver.io/en/latest/reference/profileservice.html) suggest implementing the IProfileService interface for this, which I've done. I want the ProfileService to call the web app's API to retrieve the information about the user to avoid forcing the IdentityServer to need to know about/directly access the database. My problem however, is that calling the API though needs an access token.
Is it possible to retrieve the token for the current user inside the ProfileService's IsActiveAsync / GetProfileDataAsync methods? I can't find solid documentation that identifies if the token is even generated at that point. I'm also a total noob when it comes to authentication/authorization, it's a massive topic!
I had the idea of using the client credentials flow inside the ProfileService to call the API, just to populate that initial token. However, I don't know whether or not that's an absolutely terrible idea... or if there are any better concepts someone could refer me to that I could investigate.
Can anyone point me in the right direction?
Have a look at ITokenCreationService that is part of identityserver4. You can inject that service into your IProfileService implementation and then create a new bearer token with any claims you like.
For example:
protected readonly ITokenCreationService _tokenCreationService;
...
var token = new Token
{
AccessTokenType = AccessTokenType.Jwt,
Issuer = "https://my.identityserver.com",
Lifetime = (int)TimeSpan.FromMinutes(5).TotalSeconds,
Claims = GetClaimsNeededForApiCall()
};
string myToken = await _tokenCreationService.CreateTokenAsync(token);
...
This is not possible to retrieve the access_token for a user within ProfileService.
The profile service is called whenever IdentityServer needs to return claims about a user. This means if you try to generate a token for the user within ProfileService it will call the ProfileService again.
I would like some clarification on how to use ID tokens and access tokens in an implicit grant flow.
I have an Angular SPA using MSAL.js for Angular package and a Web API. The API does NOT call any external services, like MSFT Graph. The back end uses role claims from JWT to establish RBAC authorization to our API functionality only.
This doc says:
https://learn.microsoft.com/en-us/azure/active-directory/develop/id-tokens
id_tokens are sent to the client application as part of an OpenID Connect flow. They can be sent along side or instead of an access token, and are used by the client to authenticate the user.
ID Tokens should be used to validate that a user is who they claim to be and get additional useful information about them - it shouldn't be used for authorization in place of an access token.
This doc shows an authentication flow where a web API is called with an ID token instead of an access token:
https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-implicit-grant-flow
This sample code sends ID token too (calling own Web API sample):
https://github.com/Azure-Samples/active-directory-javascript-singlepageapp-dotnet-webapi-v2
The sample back end validates ID token audience against Client ID of the app that issued it.
Given our API is the only consumer of tokens, should we use ID tokens or access tokens?
Generally speaking, when securing your API with the Microsoft Identity platform, clients should be using the access token when making API requests, not the ID token.
check this part
It first calls acquireTokenSlient, which gets a token from the cache if available if not
it calls acquireTokenPopUp which will get an acces token for the specific scope, I am not sure if it would open up a popup window or will get a token in the background with hidden iframe. But it would fetch an access token for sure. API can never be accessed with ID token.
check https://learn.microsoft.com/bs-latn-ba/azure/active-directory/develop/scenario-spa-acquire-token for more clarification
I am a bit confused about the use case with reference tokens and the introspection endpoint in a MVC/Webforms scenario.
My basic question is why is the introspection endpoint only setup to allow authentication requests from ApiResource credentials, (api1/apisecret) and not allow for client credentials? I am able to use the code bellow with ApiResource credentials, I just get unauthorized with client credentials. I think this is as designed.
I want to give our systems admins the ability to revoke the tokens and force logout users. My plan is to use the CookieAuthenticationProvider’s OnValidateIdentity to periodically validate the reference tokens and force a logout if needed. I think that I can get similar functionality with short lived self-contained JWT tokens and refresh tokens but liked the simplicity of using reference tokens. I can obviously use two sets of credentials in the same application, one for the client and one ApiResource, but is seams more intuitive to use/manage one set of credentials, the client’s. Should I not use reference tokens for a server-side trusted application?
My clients are setup like the following.
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
AccessTokenType = AccessTokenType.Reference,
Here are a few notes from my research.
Here are the docs about Introspection Endpoint - http://docs.identityserver.io/en/release/endpoints/introspection.html
Here is Dominick Baier's post about reference tokens - https://leastprivilege.com/2015/11/25/reference-tokens-and-introspection/
Here is an example of the IntrospectionClient call I am using. https://github.com/IdentityServer/IdentityServer4.Samples/blob/release/Clients/src/ConsoleIntrospectionClient/Program.cs
Here is the spec for introspection and as you can see it does not indicate the type of authentication required. - https://www.rfc-editor.org/rfc/rfc7662
To prevent token scanning attacks, the endpoint MUST also require
some form of authorization to access this endpoint, such as client
authentication as described in OAuth 2.0 [RFC6749] or a separate
OAuth 2.0 access token such as the bearer token described in OAuth
2.0 Bearer Token Usage [RFC6750]. The methods of managing and validating these authentication credentials are out of scope of this
specification.
This is a sample of a ApiResource with a secret.
// simple version with ctor
new ApiResource("api1", "Some API 1")
{
// this is needed for introspection when using reference tokens
ApiSecrets = { new Secret("apisecret".Sha256()) }
},
Here is the sample client form the ConsoleIntrospectionClient client.
new Client
{
ClientId = "roclient.reference",
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
AllowedScopes = { "api1", "api2.read_only" },
AccessTokenType = AccessTokenType.Reference
},
Does not having a secret defined for an IdentityServer4 API Resource introduce a security vulnerability?
I'm a little confused on the Introspection Endpoint, when it is used, and whether or not someone could use the Introspection endpoint to bypass Authorization and access an API without a defined secret (by a POST with just the API name as a parameter).
Is this possible? Or is the introspection endpoint only authorized through defined clients that use something like the Client Credential Grant?
The introspection endpoint will only validate a posted token, it shouldn't accept an API name in its request.
It can be used to validate reference tokens (or JWTs if the consumer does not have support for appropriate JWT or cryptographic libraries). The introspection endpoint requires authentication using a scope secret.
http://docs.identityserver.io/en/release/endpoints/introspection.html
This shouldn't be an endpoint you need to implement, it is included by identity server in the same way as the '.well-known/openid-configuration'.
A use case for this endpoint would be an API being passed a token and wanting to confirm its genuine and still valid (not expired or revoked), the response would include the claims associated with the token (users claims with the tokens scope taken into consideration)
For introspection security considerations see the RFC 7662 Section 4
An API Resource requires a Client Secret when you are using the Authorization Code Flow as its sending claims on the back channel.
If you are using Reference tokens you will also require a Client Secret, as the access token is never presented to the client, but instead the reference token is passed from the client to the api resource, and then onto the identity server in exchange for the access token.
It really depends on the client and the flow.
I have an MVC site with an embedded angular client and I've recently implemented an anti forgery XSRF token as a security measure.
I have set it up in Startup.cs as follows:
services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");
app.Use(next => context =>
{
if (string.Equals(context.Request.Path.Value, "/", StringComparison.OrdinalIgnoreCase) ||
string.Equals(context.Request.Path.Value, "/index.html", StringComparison.OrdinalIgnoreCase))
{
// We can send the request token as a JavaScript-readable cookie, and Angular will use it by default.
var tokens = antiforgery.GetAndStoreTokens(context);
context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken,
new CookieOptions() { HttpOnly = false });
}
return next(context);
});
And I've implemented it within my angular front-end like so:
{ provide: XSRFStrategy, useFactory: xsrfFactory}
export function xsrfFactory(): CookieXSRFStrategy {
return new CookieXSRFStrategy('XSRF-TOKEN', 'X-XSRF-TOKEN');
}
And protecting my controllers like:
[Authorize] //Validation of AzureAD Bearer Token.
[ValidateAntiForgeryToken]
public class UserController : Controller
It is intended that the X-XSRF-TOKEN header be validated with any call to my API, and this works successfully for all calls in the original session. However, my app uses Adal to log the user in, and after the redirect from a successful login, this validation step fails and I receive a 400 from my API for any subsequent calls.
The original X-XSRF-TOKEN header is still sent with all outgoing requests from my angular client after the login so I suspect it must be that my server side no longer has the token to validate against, or my server has generated a new one and my client doesn't retrieve it. But for whatever reason it breaks down and it's very hard to debug without creating some custom filter so I can see what's going on inside it.
Is there a way to reset this token after a client side redirect so that both my server and client share common knowledge of it again? Or should I be generating the token in my Index.html for example?
EDIT
Edited controller decoration above for missing [Authorize] attribute.
So my controller is protected by a step validating the AzureAD Bearer token as well as the Anti-Forgery validation. Removing the AzureAD Validation as a test did not resolve the issue, oddly.
Error on failing API calls displays in output after Adal login as:
The provided anti-forgery token was meant for a different claims-based user than the current user.
Based on my understanding, you were protecting the controller using token. For this issue is expected, you can refer the progress of validate the anti-XSRF tokens from below(refer this link):
To validate the incoming anti-XSRF tokens, the developer includes a ValidateAntiForgeryToken attribute on her MVC action or controller, or she calls #AntiForgery.Validate() from her Razor page. The runtime will perform the following steps:
The incoming session token and field token are read and the anti-XSRF token extracted from each. The anti-XSRF tokens must be identical per step (2) in the generation routine.
If the current user is authenticated, her username is compared with the username stored in the field token. The usernames must match.
If an IAntiForgeryAdditionalDataProvider is configured, the runtime calls its ValidateAdditionalData method. The method must return the Boolean value true.
Since you were developing the SPA application with back-end web API, when the request to the web API, it will always issue the anti-XSRF token with no identity. And when you send the request to the back-end with anti-XSRF and Azure AD token, this time the web API already authenticate the request via the Azure AD token. And it will always return false when checking anti-XSRF token to match the identity information.
In this scenario, if the back-end only using the bear token authentication and store the token with session storage, there is no need to enable XSRF prevention since others is not able to steal the token and forge the request.
If your back-end also support the cookie authentication or basic auth, NTLM etc, you can disable the identity checking by adding the following to your Application_Start method: AntiForgeryConfig.SuppressIdentityHeuristicChecks = true.(refer this link)
More detail about XSRF/CSRF abouth oauth and web API, you can refer the threads below:
How does ValidateAntiForgeryToken fit with Web APIs that can be accessed via web or native app?
AntiForgeryToken does not work well with OAuth via WebAPI
Try replacing [ValidateAntiForgeryToken] with [AutoValidateAntiforgeryToken]
https://github.com/aspnet/Antiforgery/blob/dev/src/Microsoft.AspNetCore.Antiforgery/Internal/DefaultAntiforgeryTokenGenerator.cs