Keeping Downstream API JWT Updated - .net-core

My .NET Core API calls a downstream API which is secured by JWT, obtained by client_credentials OAuth grant-type authentication. That JWT is only valid for one hour and needs to be refreshed. The downstream API is exposed internally in a singleton service and the service clients should not care about the token refresh.
A naïve implementation (which I currently have) is, that the service checks if the current JWT is valid and if not, refreshes the JWT with the given credentials. That JWT is then cached for 3600 (or a little bite before) seconds and the same thing happens again.
This works well for a low "traffic" situations where there will be only one service call triggering this refresh. However, on high "traffic" scenarios this creates a bottleneck, where multiple requests will wait for that authentication to complete (or worse if I would allow this service to be used stateless and in parallel, that the JWT refresh is done multiple times).
I played around with a Background Service which refreshes the token on start-up and then on each interval (minus the grace time) and only exposes the actual token to the downstream API service. However this seems overly complicated which boils down to the actual question:
What are best practices for refreshing time-bound downstream API keys?
Are there any out of the box solutions in the .NET Core world for this? I've found Microsoft.Identity.Web but it only works around Azure (which is not possible in my case).

Use IdentityModel.AspNetCore
services.AddAccessTokenManagement(options =>
{
options.Client.Clients.Add("identityserver", new ClientCredentialsTokenRequest
{
Address = "https://demo.identityserver.io/connect/token",
ClientId = "m2m.short",
ClientSecret = "secret",
Scope = "api" // optional
});
});
services.AddHttpClient<MyClient>(client =>
{
client.BaseAddress = new Uri("https://demo.identityserver.io/api/");
})
.AddClientAccessTokenHandler();

Related

How to use end client state parameter in IdentityServer 3?

I have configured IdentityServer 3 to use external IdentityProvider which is pointing to AAD.
As of now, when I send a request to IdentityServer, I am properly redirected to the AAD for login, however, the 'state' parameter that I am sending to IdentityServer is overridden, and the value of OpenIdConnect.AuthenticationProperties is encrypted and sent to the AAD as the state in the query string.
For eg:
https://localhost:44333/idpaad/connect/authorize?client_id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx&redirect_uri=https://localhost:44394/&response_mode=query&response_type=code&scope=openid%20email&state=9b0e82c3-e623-42f1-bede-493243c103e7
Here,
https://localhost:44333/idpaad/connect/authorize -> IdentityServer endpoint
state=9b0e82c3-e623-42f1-bede-493243c103e7 -> client generated GUID sent as querystring.
when I see in the "RedirectToIdentityProvider" middleware in the StartUp.cs of IdentityServer OpenIdConnectAuthenticationNotifications, the value of state is updated to
OpenIdConnect.AuthenticationProperties=(protected values) instead of the GUID and the same is also returned as a query string back to the Redirect URI.
enter image description here
Is there a way to send the original state and not override it by IdentityServer3?
While using wsFederation, I am not getting this issue and the same is forwarded directly to the IdP.
Any help is deeply appreciated.
Most of the time it's advisable for an Azure Active Directory integrated application to maintain an application state when sending request to Azure AD for login. And the recommended way to achieve this is to use the ‘state’ parameter as defined in the OpenID Connect standards.
If you check this document form OpenID, you will find that primary reason for using the state parameter is to mitigate CSRF attacks.
RECOMMENDED. Opaque value used to maintain state between the request and the callback. Typically, Cross-Site Request Forgery (CSRF, XSRF) mitigation is done by cryptographically binding the value of this parameter with a browser cookie.
The ‘state’ parameter is used for both preventing cross-site request forgery attacks and to maintain user’s state before authentication request occurs.
In an ASP.NET or ASP.NET CORE web application using OpenID Connect OWIN middleware, the ‘state’ parameter is maintained automatically by the middleware when sending out an authentication request, this is the only reason you are seeing the state parameter getting overridden in your case.
But if you want you can add custom data in your state parameter. Use the following code in OpenIdConnectNotifications’s RedirectToIdentityProvider event to inject custom data into the ‘state’ parameter.
var stateQueryString = notification.ProtocolMessage.State.Split('=');
var protectedState = stateQueryString[1];
var state = notification.Options.StateDataFormat.Unprotect(protectedState);
state.Dictionary.Add("MyData","123");
notification.ProtocolMessage.State = stateQueryString[0] + "=" + notification.Options.StateDataFormat.Protect(state);
Check this document and Microsoft identity platform and OpenID Connect protocol for detailed information.

Blazor WebAssembly Standalone access multiple AAD protected APIs

I have managed to make default template work (my blazor standalone SPA should acquire tokens for several scopes from different ADApps - webAPIs; I've managed to get token only for one scope at the time even if I defined additionalScopes or defaultaccesstokenscopes).
builder.Services.AddMsalAuthentication(options =>
{
var config = options.ProviderOptions;
config.Authentication.Authority = "https://login.microsoftonline.com/tenantID";
config.Authentication.ClientId = "clientID";
options.ProviderOptions.DefaultAccessTokenScopes.Add("offline_access");
options.ProviderOptions.DefaultAccessTokenScopes.Add("https://graph.microsoft.com/user.read");
options.ProviderOptions.DefaultAccessTokenScopes.Add("https://tenant.crm.dynamics.com/user_impersonation");
options.ProviderOptions.DefaultAccessTokenScopes.Add("clientID/scope1");
// tried this too
// config.AdditionalScopesToConsent.Add("https://tenant.crm.dynamics.com/user_impersonation");
});
Now there is a question on how to get the other tokens because it gets the token only for 'clientID' scope if multiple scopes are mentioned...? and use those tokens from wasm page in HttpClient request?
In angular (with MSAL) this is all done automatically, you define scopes you want and it gets all the tokens. Then it intercepts all requests and adds authorization header and corresponding token by domain of the request.
Is there similar mechanism here or should this be done manually by adding corresponding token for every request and using HttpRequestMessage with HttpClient.SendAsync()?
Obviously for business application there is not much of a use without contacting some kind of protected API, which is usually an app in the same AAD. For example let's say it can be a simple query to the Dynamics CRM's webapi.

Xamarin Forms Azure App Service ADAL Logout not working as expected

We are currently writing a Xamarin Forms Azure Mobile application, using client flow, AAD authentication, refresh tokens etc.
Most of this is working as expected. However, logging out of the application does not work properly. It completes the logout process for both Android and iOS - but upon redirection to the login screen, hitting sign in will never prompt the user with the Microsoft login as expected, it will sign them straight back into the app.
To add a little bit of background, this app has been implemented as per Adrian Hall's book,
current link: https://adrianhall.github.io/develop-mobile-apps-with-csharp-and-azure/
with the above described options and configurations.
I have also read through the 30 days of Zumo (also by Adrian Hall) blog on this, and every single post I can find on here relating to this.
My current logout code is as follows:
public async Task LogoutAsync()
{
var loginProvider = DependencyService.Get<ILoginProvider>();
client.CurrentUser = loginProvider.RetrieveTokenFromSecureStore();
var authUri = new Uri($"{client.MobileAppUri}/.auth/logout");
using (var httpClient = new HttpClient())
{
if (IsTokenExpired(client.CurrentUser.MobileServiceAuthenticationToken))
{
var refreshed = await client.RefreshUserAsync();
}
httpClient.DefaultRequestHeaders.Add("X-ZUMO-AUTH", client.CurrentUser.MobileServiceAuthenticationToken);
await httpClient.GetAsync(authUri);
}
// Remove the token from the cache
loginProvider.RemoveTokenFromSecureStore();
//Remove the cookies from the device - so that the webview does not hold on to the originals
DependencyService.Get<ICookieService>().ClearCookies();
// Remove the token from the MobileServiceClient
await client.LogoutAsync();
}
As far as I can tell, this includes everything I have found so far - i.e. calling the /.auth/logout endpoint, removing the token locally, clearing the cookies from the device (as we log in inside a webview) and lastly calling the LogoutAsync() method from the MobileServiceClient.
Am I missing anything? Or is there a way we can force log out from this environment? As I know you can't "invalidate" an OAuth token, you have to wait until it expires - but to my mind, the /.auth/logout endpoint is supposed to handle this within the Azure environment? Though I'm just not sure to what extent.
Any help is appreciated.
We are currently writing a Xamarin Forms Azure Mobile application, using client flow, AAD authentication, refresh tokens etc. Most of this is working as expected. However, logging out of the application does not work properly.
I assumed that if you use the server flow for logging with AAD, the logout processing may works as expected. As you described that you used client flow, since you have clear the client cache for token, I assumed that the issue may caused by the LoginAsync related (ADAL part) logic code, you need to check your code, or you could provide the logging related code for us to narrow this issue.

IdentityServer4 without redirects

I'm implementing IdentityServer4 as my authentication service.
The client that will use this is an Angular app. From all the examples I've seen, the client is redirected to a page hosted on the identity server which is returned back to the client afterwards.
For user experience, I would like to keep the user on my page the whole time. So this leads to a couple of questions:
Can the identity server UI be displayed in a DIV or an iframe within a site? I'm guessing that iframe is a bit frowned upon?
If the above is not possible, is it possible to host the login UI on the client app, not the identity server?
I suppose it's more of a question for the UX group, but I would have thought that keeping the user within the site would lead to a better user experience instead of redirecting them completely?
The UX question depends on a number of things, and UX has to be tempered by security considerations.
If you completely own the client, and the identity server, you could use the ResourceOwnerPasswordFlow which doesn't involve redirects and allows your client to take the username/password and obtain an access token using them.
This kind of flow though is not recommended if you don't own the client, and/or don't trust it with the credentials. Imagine the situation where a website delegates their login process to something like Google/Facebook ... as the owner of the identity (Google/Facebook) you really wouldn't want your customers entering their password into some random website. Instead you would use the redirect flows to present a familiar and trusted URL that the customer would be happier to enter their details into.
So the question of redirects isn't a simple 'it gives bad UX', because in many cases it is a better UX to involve redirects if that brings with it an enhanced sense of security.
I've seen IdentityServer4 projects configured to work with SPAs, although I don't have all the details here off the top of my head.
Check out this piece of the documentation:
Defining browser-based JavaScript client (e.g. SPA) for user
authentication and delegated access and API This client uses the so
called implicit flow to request an identity and access token from
JavaScript:
var jsClient = new Client {
ClientId = "js",
ClientName = "JavaScript Client",
ClientUri = "http://identityserver.io",
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
RedirectUris = { "http://localhost:7017/index.html" },
PostLogoutRedirectUris = { "http://localhost:7017/index.html" },
AllowedCorsOrigins = { "http://localhost:7017" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
"api1", "api2.read_only"
} };
You can see all the redirects go back to the same URL. Presumably, your routing would take over from there.

HMAC and WCF Service .net

So I'm very new with HMAC authentication and I really don't know what I'm doing nor reading atm.
I've been trying to understand the following articles / links / discussions properly:
How to implement HMAC Authentication in a RESTful WCF API
http://blogs.microsoft.co.il/blogs/itai/archive/2009/02/22/how-to-implement-hmac-authentication-on-a-restful-wcf-service.aspx
http://buchananweb.co.uk/security01.aspx
With that said I have a few questions:
Understanding the first link, if for example I have a loginAuthentication service created in .net and will be accessed from an iPhone app do I pass an unencrypted username (message) for this and should return just a true / false or should it return an encrypted string in which I will be using later on for other transactions (Delete, Insert services, etc)?
[ServiceContract]
public partial class LoginService
{
[OperationContract]
bool Authenticate(string username) {
// stuffs
}
}
With that said, after I verified the user, and this is where I get lost. Is it better that I save something in the database 'with a timestamp' (someone told me about this and I read some discussions about this too)? Or do I just return it with the encrypted message (dependent on the first question) so that everytime a request is made the timestamp is already attached?
a. And what do I do with that timestamp?
b. Is it going to be used once the message is sent again for another transaction?
Keys and secret message. The way I understood it is that the key will be the password of the user. So if the user sends his username I can open the message using the password of that user? This makes sense if the user already has a session and is just requesting to get data or requesting for a delete, insert, etc. Should it still be the same way if it's just authenticating the username and password of the user?
Thank you for your time!
The first thing I would like to mention is that the WCF Web Api was a beta project which is no longer being developed. It was replaced by ASP.NET Web API which is an awesome framework for developing RESTful services.
If you want to get a good idea how a RESTful service and authentication works the Netflix API would be a great place to start. They have a lot of documentation regarding the security portion and this helped me understand HMAC a lot more.
HMAC creates a hash using a secret key. The client and server both maintain a copy of the secret key so that they can generate matching hashes. This allows you to 'sign' a request which serves as both authentication (you know the person sending it is who they say they are), and message integrity (knowing the message they sent is the original message and has not been tampered with).
A signature is created by combining
1. Timestamp (unix epoc is the easiest to send in urls)
2. Nonce (a random number that can never be used twice to protect against someone re-using it)
3. Message (for a GET request this would be the URL, a POST would be the whole body)
4. Signature (the three previous items combined and hashed using the secret key)
Each of the above can be sent in the query string of the request, then the server can use the first 3 and their copy of the secret key to recreate the signature. If the signatures match then all is good.
In a RESTful API that is over plain HTTP (not using HTTPS over an ssl), I would sign every request sent because again this authenticates and provides message integrity. Otherwise if you just send an authentication token you know the user is authenticated but how do you know the message was not tampered with if you do not have a Message Digest (the HMAC hash) to compare with?
An easy way to implement the server-side checking of the signature is to override OnAuthorization for System.Web.Http.AuthorizeAttribute (Make sure not to use Mvc autorize attribute). Have it rebuild the signature just as you did on the client side using their secret key, and if it does not match you can return a 401. Then you can decorate all controllers that require authentication with your new authorize attribute.
Hopefully this helps clear up some of your confusion and does not muddy the water even further. I can provide some more concrete examples later if you need.
References:
Netflix Api Docs: http://developer.netflix.com/docs/Security#0_18325 (go down to the part about creating signatures, they also have a link which shows a full .NET example for creating the HMAC signature)
.NET class for creating HMAC signatures http://oauth.googlecode.com/svn/code/csharp/OAuthBase.cs
Netflix API Wrapper I wrote: https://bitbucket.org/despertar1318/netflix-api/overview
ASP.NET Web API: http://www.asp.net/web-api
Looking at your questions in turn
...do I pass an unencrypted username (message) for this and should return just a true / false or should it return an encrypted string in which I will be using later on for other transactions (Delete, Insert services, etc)?
If you just returned a boolean, you'd have no way to then match the authentication request to subsequent requests. You'll need to return some sort of authentication indicator, on a classic website this would be the session cookie, in your instance you want to pass a value that will act as shared key.
Is it better that I save something in the database 'with a timestamp'? Or do I just return it with the encrypted message so that everytime a request is made the timestamp is already attached?
Back to the session analogy, you want to store the key from question one somewhere (the database?) with a timestamp that indicates the life of the session/validity of the key. If it's forever then I wouldn't bother with the timestamp, if it's anything else you'll need something to say when it expires.
The way I understood it is that the key will be the password of the user. So if the user sends his username I can open the message using the password of that user? This makes sense if the user already has a session and is just requesting to get data or requesting for a delete, insert, etc. Should it still be the same way if it's just authenticating the username and password of the user?
This is where the HMACing happens. You have your shared secret, you have a message, this is how I usually combine it all together.
Use all of the message as the body of data to be hashed (that way you can be sure that someone's not just copied the hash and part of the message). Hash the body of the message using the key we shared in step one. You could salt this if wanted, I'd use the username.
Finally make sure the message contains a timestamp (UTC preferably), this way you can help prevent replaying the message later. The service that's responding to the message can compare the timestamp to what it thinks the time is. If it falls outside given bounds, fail the message. Because the timestamp will be part of the HMAC, someone can't just update the date and replay the message, the hashes won't match as soon as the message is tampered with.

Resources