We are using Identity Server4 to protect our APIs. We want to use Refresh tokens to allow gaining long lived access to APIs.As per the documents("http://docs.identityserver.io/en/release/index.html") we have set the AllowOfflineAccess to true but still its not working. After AccessTokenLifeTime expire(3600 seconds), token not working. Here is the client:
return new List<Client>
{
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" },
AccessTokenLifetime=3600,
AllowOfflineAccess=true
}
The client app has to request the refresh token.
Please note that refresh tokens are not available for every flow:
Refresh tokens are supported in hybrid, authorization code and
resource owner password flows. To request a refresh token, the client
needs to include the offline_access scope in the token request (and
must be authorized to request for that scope).
Add this line to your client code:
.AddOpenIdConnect("oidc", "Open Id connect", options =>
{
options.Scope.Add("offline_access");
}
The way refresh tokens work:
Login to get an access token. The refresh token is included when you use the 'offline_access' scope.
Use the access token untill it expires.
Get a new access token by sending the refresh token to the endpoint
Depending on your strategy you can also 'refresh' the refresh token itself (replace the persisted refresh token with a new token). Or do not return a refresh token untill the token expires, having the user to login again.
Related
I'm building a react frontend application with a spring backend that is secured with azure ad.
I can't get the authentication flow to work.
In azure ad, I have registered 2 applictions:
API: Default configurations and under "Expose an API" I have added a scope with api://xxxx-api/Access.Api and also added the client application. Under "App Roles" I have added the roles "User" and "Admin". I have assignes both roles to myself.
Client: Registered as SPA with redirect to http://localhost:3000 where the react app is running. Did not check the two boxes for the token to enable PKCE. Under "API permissions" I added the "Access.Api" scope from the api app and granted admin consent.
In the react app I'm using #azure/msal-browser and #azure/msal-react.
My authConfig looks like this:
Then I'm just using useMsalAuthentication(InteractionType.Popup); to sign the user in.
All this works as expected and I'm getting a token back. If I parse this token in jwt.io,
I get "iss": "https://sts.windows.net/42xxxxx-xxxxx-xxxxxx-xxxxx/",
"scp": "openid profile User.Read email", "ver": "1.0",.
However, I do not see the scopes or roles for my API app.
I'm then using an Axios request interceptor to provide the bearer token on every API request:
const { instance, accounts } = useMsal();
const account = useAccount(accounts[0]);
axios.interceptors.request.use(async (config) => {
if (!account) {
throw Error('No active account! Verify a user has been signed in.');
}
const response = await instance.acquireTokenSilent({
...loginRequest,
account,
});
config.headers.Authorization = `Bearer ${response.accessToken}`;
return config;
});
The token is successfully added to the header of each request.
My spring application however fails to validate this token.
My spring config:
I could implement the token validation myself if that is an issue here, but how do I fix, that the bearer token does not contain the roles that I need to check if the user has access to specific resources on the api?
I figured it out.
Basically the scopes openid, email and profile should only be used when requesting an ID token. The ID token contains all roles exposed in the client app.
If these scopes are used when requesting an access token, the token will contain no roles or scopes at all. Only use the scope from the api app that is exposed in the client app when requesting an access token, and the roles will show up in the access token.
Auth Provider : Azure Active Directory
Client library : #azure/msal-react
As explained here my msal token expires after one hour MSAL token expires after 1 hour, My requirement is I would like to configure a session time of 15 minutes ( or 10 minutes) after which I wanna trigger a popup, saying please login again? Is there a way to do using msal-react.
Currently, after one hour am calling acquireTokenSilent to acquire the new token, using which client is unaware that this happened and client thinks it has infinite lifetime for the session.
Here is the implementation
export const refreshIdToken = async (msalInstance: IPublicClientApplication) => {
const account = msalInstance.getActiveAccount();
try {
if (account != null) {
const token = await msalInstance.acquireTokenSilent({
scopes: loginRequest.scopes,
account
});
return token.idToken;
}
} catch (error) {
if (error instanceof InteractionRequiredAuthError) {
return msalInstance.acquireTokenRedirect(loginRequest);
} else {
console.error(error);
}
}
};
const token = await refreshIdToken(msalInstance);// This will never expires , as it always refresh after one hour internally
axios.defaults.headers.common["Authorization"] = `Bearer ${token}`;
//api call
What are the steps I need to configure from Azure AD end or client code end?
Thanks in advance.
You can specify the lifetime of token of access, ID or SAML token issued by the Microsoft identity platform. You can set the lifetime of token for specific service principle in your organization. However, Microsoft does not currently support configuration token lifetime of Manage identity service principle.
Note: Configuration token lifetime policy only applies to mobile and desktop clients that access SharePoint online and OneDrive for business resources but id does not apply to web browser sessions.
For the new update of 2021, you cannot configure the refresh token and session token lifetime. New tokens issues after existing tokens have expired are now to set to the default configuration.
I am trying to use AAD delegated permission Bearer tokens for a Visio VSTO addin to create SharePoint Online pages using CSOM. Initially I was able to get this working entering username / password following Modern Authentication with CSOM for Net Standard
However, I would like for the user to select an existing AAD account. When I attempt to use the following code the Bearer token "aud" claim is consistently set to "00000003-0000-0000-c000-000000000000" which is the Graph API. Whilst a ClientContext object is returned I am getting a HTTP 401 Unauthorized error when performing a page lookup.
The code is as follows
//
// Get Client App
//
var ClientApp = (PublicClientApplication)PublicClientApplicationBuilder.Create(<AAD App ID>)
.WithDefaultRedirectUri()
.WithTenantId(<AAD Tenant ID>)
.WithAuthority(AzureCloudInstance.AzurePublic, <AAD Tenant ID>)
.Build();
//
// Prompt for user to select preferred AAD account
// The returned JWT Bearer Token "aud" claim is 00000003-0000-0000-c000-000000000000
//
var Token = ClientApp.AcquireTokenInteractive(scopes)
.WithPrompt(Prompt.SelectAccount)
.WithParentActivityOrWindow(GetActiveWindow())
.ExecuteAsync()
.GetAwaiter()
.GetResult();
//
// Get client Context
//
var ClientContext = AuthenticationManager.GetAzureADAccessTokenAuthenticatedContext(<SharePoint Site URL>, Token.AccessToken);
//
// Using the Client Context to query the Site results in HTTP 401
//
ClientContext.Load(ClientContext.Web, p => p.Title, t => t.Description);
ClientContext.ExecuteQuery();
Looking at the code for the AuthenticationManager class in the above link I can see that the AAD Bearer request is passing the following resource request parameter to the SharePoint online URL:
var body = $"resource={resource}&client_id={clientId}&grant_type=password&username={HttpUtility.UrlEncode(username)}&password={HttpUtility.UrlEncode(password)}";
So it seems that AAD is setting the Bearer token "aud" claim based upon this parameter. However, when I try and add this parameter using 'WithExtraQueryParameters' I am getting the following error: "AADSTS901002: The 'resource' request parameter is not supported"
Ok, I figured out the problem. The scope needs to be prefixed with the resource:
string[] scopes = { "https://<domain>.sharepoint.com/AllSites.Write", "user.read" }
Then retrieve the token
this.Token = await ClientApp.AcquireTokenInteractive(scopes)
.WithPrompt(Prompt.SelectAccount)
.WithParentActivityOrWindow(GetActiveWindow())
.ExecuteAsync();
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
},
I'm developing a MEAN App, the problem that I have is that I want to destroy the token generated by JWT. Initially I thought that the logout function would the job:
router.get('/logout', function(req, res) {
req.logOut();
res.status(200).json({
status: 'Bye!'
});
});
But now I realized that "req.logOut()" is just a passport function that doesn't do anything to the token.
I send the token from client in the header of the request, so in case the user saved the token somewhere else, when logout, the user can still have access to the app if the token is included in the request. So my questions are the following, how do I destroy the token ?, is it "stored" somewhere ? is it ever auto-destroyed ?
I did not find any method to destroy simple JWT token. But I have a suggestion to solve your issue.
You can keep the token in DB for each session. So you can validate the client request by comparing 'token from DB' with the token in 'client request header'. While logout you can remove the token form DB. So user can't access the app after logout.