JWT Authorization policy to Ignore expiration (Lifetime) - .net-core

I am working with JWT authentication in Asp.net core 3.0. The application is already setup to use JWT authentication and it is working fine. What I am looking for is to have an endpoint to be able to accessed even the auth token is expired (but all the other checks must be validated). I read about the policies and authentication schemes and played with those but it didn't help. How this can be made possible? Any help would be appreciated. TIA

That defeats the whole purpose of the system; why would you want that?
You could technically just increase the lifetime of the token, an example with IdentityServer4:
int tokenLifetime = (int)TimeSpan.FromDays(30).TotalSeconds;
services.AddIdentityServer()
.AddInMemoryClients(new[] {
new Client
{
ClientId = "client",
AllowedGrantTypes = GrantTypes.ClientCredentials,
ClientSecrets =
{
new Secret("some_secret".Sha256())
},
AllowedScopes = { "your-scope" },
AccessTokenLifetime = tokenLifetime
}
});
Another way is to use refresh tokens to renew your access token.

Related

how to handle expiring access token from partner api

I'm new to Next js. I'm sure this is a common issue but I don't know what to search for. Here's an outline:
One of my partners has an API with Bearer auth. The Bearer token comes from an endpoint I call (/auth) with my username and password. That endpoint returns the Bearer token that I use for all other endpoints, but it expires in one day.
How would I handle making API calls on Next.js API routes to this partner? I.e. where would I store this access token so each API route doesn't need to constantly fetch it. And, how do I update it when it expires?
Your clients (once authenticated) should be the ones "storing" these tokens. You would basically need to fetch it from the client's session, cookie, or however you are storing those.
As far as updating these tokens, your auth provider should also provide a "refresh token" that can be used to retrieve a new jwt once it has expired. The purpose here is that you'll be able to refresh the token for the user without requiring them to log in again.
Depending on your provider, this may be a new endpoint you'll need to call.
https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/
If you are making calls to your partner's api when you go to a specific route(like a protected one), then you should store your access token as cookie with http-only flag to avoid security issues like xss attacks that can steal the session data from your browser(the http-only flag should be set in the /auth route in your partner's api when he sends the response with the token), so make the calls within the getServerSideProps sending the cookie with the request, so your partner should take the token from the cookie and validate it to allow the request, one thing i need to point out is that you can't access an http-only cookie from client side, but as you are using nextJs you can still access it from getServerSideProps with a library called nookies, so you could do something like this:
export const getServerSideProps: GetServerSideProps = async ctx => {
const cookies = nookies.get(ctx)
const someApiData = await fetchApiData(cookies)
if (!someApiData) {
return {
redirect: {
// Redirect to home if not authorized
destination: '/',
permanent: false
}
}
}
return {
//return data fetched from the api
props: {
someApiData
}
}
}
and the function that makes the api call, could look like this(note that i'm using axios):
const fetchApiData = async (cookies: CookieData) => {
try {
const result = await axios.get<ApiData>('/some/api/route', {
// Pay attention to this line, here we are sending the cookie with the access token
headers: {
Cookie: `token=${cookies.token}; HttpOnly;`
}
})
return result.data
} catch (error) {
console.log(error)
}
}
Note that you should send the cookie whenever you make a request to a protected route and your partner should validate this token in each route that he wants to protect.
And to refresh the token without login again, your partner can implement a refresh token like #SLee mentioned. This is just an example but you got the idea.

How to make Owin automatically use refresh token when access token expires

I have an Owin client connected to IdentityServer 4, and am wondering how to get owin to request a new access_token using the refresh token. I can successfully get owin to swap the code given for an access_token, id_token and refresh_token with the following configuration:
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookie"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Authority = "http://localhost:5000",
ClientId = "mywebsite",
ClientSecret = "secret",
RedirectUri = "https://localhost:5001/",
ResponseType = "code",
RequireHttpsMetadata = false,
SaveTokens = true,
UseTokenLifetime = true,
SignInAsAuthenticationType = "Cookie",
Scope = "openid profile email offline_access",
RedeemCode = true,
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = n =>
{
Console.WriteLine(n);
return System.Threading.Tasks.Task.FromResult(0);
},
TokenResponseReceived = n =>
{
Console.WriteLine(n);
return System.Threading.Tasks.Task.FromResult(0);
}
},
});
}
Firstly, where do I save these tokens to? I can access them all the SecurityTokenValidated callback - should they go into the claims? Database? Memory?
Secondly, I have on my IdentityServer client configuration the access_token lifespan set to 60s, identity_token set to 3600s, and refresh to 30 days (please note the access_token is only this short for testing purposes). So how can I configure Owin to recognize that the access_token has expired and that it needs to go back to identityserver with the refresh_token and get a new one. Answers with example code snippets would be appreciated as my knowledge on all this is very small.
Relevant Info:
IS4 v3
.Net Framework v4.6
Client is set in IS to allow offline access
Take a look at this article:
Automatic Token Management for ASP.NET Core and Worker Services 1.0
Otherwise than that there is no logic in the AddOpenIdConnect(..) handler to deal with renewal of refresh tokens. I think its up to your application to refresh them. Refreshing them in code is not that hard if you have saved the refresh token somewhere safe.
See this question How to use 'refresh_token' in IdentityServer 4?

What is the expiry date time of the access token?

When we implement the Client Credentials grant - Protecting an API using Client Credentials how long is the access token usable for (e.g. whats is the expiry date) before the client needs to generate a new access token?
When you create a client you can define the lifetime of an access token
var myClient = new Client
{
ClientId = "testClient",
AllowedGrantTypes = GrantTypes.ClientCredentials,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedScopes = { "api1" },
AccessTokenLifetime=3600
};
If you do not supply AccessTokenLifetime then it will default to 3600 which is an hour. This means it will expire one hour after it is created
FYI,
The expiry of a JWT will be represented as Unix epoch.
For instance
{
"exp":1517988642,
...
}
which can be checked in a tool such as https://www.epochconverter.com/

Identityserver4 with ADFS 4.0, cant get Userinfo or Claims

i have tried to configure my Identityserver4 to use ADFS 4.0 as an external Provider.
I have configured it as followed:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
ClientId = "60b3d106-d155-4f9f-ba75-84b8078829fa",
AuthenticationScheme = "oidc",
PostLogoutRedirectUri = "http://localhost:5000",
DisplayName = "ADFS TEST Domain",
MetadataAddress = "https://srv2016dc01.test.local/adfs/.well-known/openid-configuration",
SaveTokens = true,
GetClaimsFromUserInfoEndpoint = true
});
Seems to work, as i can login and get a few Claims back:
Claims get back from ADFS
But it doesn't look like that the Userinfo Endpoint will be called...
Otherwise ther schould be more claims, and i can't see a call to the Userinfo Endpoint in the DEbug log of the ADFS Server.
I have also tried to call the Userinfo Endpoint in Code like in this link:
ASP.NET Identity (with IdentityServer4) get external resource oauth access token
But at "await _signInManager.UpdateExternalAuthenticationTokensAsync(info);" i don't get an access_token back, only an id_token...
Does anybody have an working example on Identityserver4 with ADFS 4.0 or at least with any other external OpenIdConnect Server?
Is there any other way to get all the Infos from Userinfo Endpoint?
I need to get the Group Memeberships from the authenticated User as Role Claims, for grant permissions on an WebApi Resource.
If i set "token id_token" as ResponseType Value in the Options, i'll get the tokens!
ResponseType = "code id_token"
If an access_token is available the UserInfo Endpoint will be called.
But now i'll get a 401 Error from the Userinfo Endpoint.
Anyway... This issue is resolved by adding
ResponseType = "code id_token"
to the OpenIdConnectOptions

Single Page Application with adal.js and external web api (with AAD authentication)

I have an ASP.NET SPA with a adal-js based authentication, and an ASP.NET Web Api website with Azure Active Directory auth
Both websites are hosted on Azure, on different hostnames, say
https://foo.azurewebsites.com/ and https://fooapi.azurewebsites.com/
The Web Api website auth is configured as
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
TokenValidationParameters = new TokenValidationParameters() { ValidAudience = ConfigurationManager.AppSettings["ida:Audience"] },
Tenant = ConfigurationManager.AppSettings["ida:Tenant"]
});
}
}
and Main SPA adal.js is initialized as:
var config = {
instance: "https://login.microsoftonline.com/",
tenant: "mytenant",
clientId: "client id of foo registration",
postLogoutRedirectUri: "https://foo.azurewebsites.com/",
cacheLocation: "localStorage"
};
authContext = new AuthenticationContext(config);
// Check For & Handle Redirect From AAD After Login
var isCallback = authContext.isCallback(window.location.hash);
authContext.handleWindowCallback();
var errorMessage = authContext.getLoginError();
if (isCallback && !authContext.getLoginError()) {
window.location = authContext._getItem(authContext.CONSTANTS.STORAGE.LOGIN_REQUEST);
}
// Check if View Requires Authentication
if (!authContext.getCachedUser()) {
authContext.config.redirectUri = window.location.href;
authContext.login();
return;
}
The Tenant is the same for foo and fooapi, the client id is different (one for each app registration).
The authentication flow in the foo web app is performed successfully, but every http request to fooapi returns 401 unauthorized.
How can I make fooapi share the successful authentication of foo ?
Thank you for any hint
You can use the implicit grant flow in AAD so that an ID Token is received and sent in auth header when API call is made. See below links for the details and sample code.
https://azure.microsoft.com/en-gb/documentation/articles/active-directory-authentication-scenarios/#single-page-application-spa
https://github.com/Azure-Samples/active-directory-angularjs-singlepageapp
How you acquire the access token for the web API?
To make sure the request successfully, you need to acquire the token using the resource you config in web API. You can pass the token from here to check whether the aud claim is equal to the value ida:Audience.
And also make sure the token is issued from the tenant you config in web API project since you didn't ignore the tenant verification.
Please configure your web point into endpoints and add it to initialization.
var endpoints = {`enter code here`
"https://yourhost/api": "b6a68585-5287-45b2-ba82-383ba1f60932",
};
adalAuthenticationServiceProvider.init(
{
// Config to specify endpoints and similar for your app
tenant: "52d4b072-9470-49fb-8721-bc3a1c9912a1", // Optional by default, it sends common
clientId: "e9a5a8b6-8af7-4719-9821-0deef255f68e", // Required
//localLoginUrl: "/login", // optional
//redirectUri : "your site", optional
endpoints: endpoints // If you need to send CORS api requests.
},
$httpProvider // pass http provider to inject request interceptor to attach tokens
);

Resources