I have a C# web API REST services back-end, I provide services for a CMS, a web page, and Angular2 app (this is the relevant here). The Angular app needs authenticate through the back-end sending username and password (raw credentials), the backend uses these to request an access_token to Azure Active Directory (with UserCredentials
), and sends the access_token back to the Angular app, to be used for authorization in it's requests (Authorization: Bearer
). This is the way I'm authenticating:
UserCredential uc = new UserPasswordCredential(user, password);
AuthenticationContext authContext = new AuthenticationContext(Constants.authority, false);
AuthenticationResult result = authContext.AcquireTokenAsync(Constants.audience, Constants.clientIdNative, uc).Result;
The thing is that it generates a one hour cache for that token, and if the user logs out and enters again just with the user or user + wrong password the Authentication just look up the cache for that user and ignores or doesn't validate the password. In the one hour window anyone can get in just with the username.
I found here and on many other sites ways of logging out or clearing the token, but those are not applicable for my backend because it's stateless. I don't manage sessions or HTTP contexts between these. If anyone can guide to solve this, I'm using the last assembly of Microsoft.IdentityModel.Clients.ActiveDirectory
, version 3.13.1.846, I know the method AcquireTokenByAuthorizationCodeAsync
doesn't look up for cache but there's no implementation for use with raw credentials.
Thanks guys and hope you can help me.
The strategy you are following is highly recommended against. It is generally bad practice for your application to collect a username and password at all. This flow (the Resource Owner Password Credentials (ROPC) Grant flow) is only intended for scenarios where other mechanisms are unavailable. From the OAuth 2.0 spec (emphasis added):
1.3.3. Resource Owner Password Credentials
The resource owner password credentials (i.e., username and password)
can be used directly as an authorization grant to obtain an access
token. The credentials should only be used when there is a high
degree of trust between the resource owner and the client (e.g., the
client is part of the device operating system or a highly privileged
application), and when other authorization grant types are not
available (such as an authorization code).
Even though this grant type requires direct client access to the
resource owner credentials, the resource owner credentials are used
for a single request and are exchanged for an access token. This
grant type can eliminate the need for the client to store the
resource owner credentials for future use, by exchanging the
credentials with a long-lived access token or refresh token.
When using this flow against Azure AD, you'll find that this flow will often fail, as users are sometimes required to provide more than just a username and password. Some examples of where this won't work:
- The user needs to consent to the permissions the application is requesting
- The user's password expired and needs to be changed
- The user's credentials are suspected of being compromised
- The user's sign-in is otherwise considered suspicious (more likely in your scenario, since the sign-ins will appear to be coming from web server, which is likely not located where the user is)
- The user has multi-factor authentication enabled
- The user is part of a federated domain name (where the authoritative identity provider is not Azure AD)
Basically, by using this approach, you are bypassing most of the security improvements provided by OAuth 2.0 and Azure AD, and you are putting yourself, your application and your users at risk by using the username/password flow in a way that it was not intended to be used.
Also, while the Azure AD service does currently support the ROPC flow, you'll find that support for this flow is actually being removed from libraries in some cases (e.g. Issue #320 Remove non-interactive auth from iOS/Android/WinRT and Issue #462 PCL UserCredential no longer supports password).
To answer your specific question
(Hover to see what you should NOT do in your case, but answers your question about token caching.)
One way to skip token caching is to simply use the constructor signature that takes a TokenCache
value and pass in a null TokenCache
value:
AuthenticationContext authContext =
new AuthenticationContext(Constants.authority, false, null);
A better (but still really bad) approach
(Really, don't do this, you may as well skip to the next section...)
If you find yourself in a situation where your native client app absolutely MUST use the username/password flow, and you accept the risks and are willing to live with them and all the other shortcomings, your Angular app should be making the token request to Azure AD directly, not passing through your API back-end. The received access token can then be used to make authenticated requests against your API (or other APIs which are secured by Azure AD, depending on what you specify as the resource
in the token request).
The right approach
The correct approach is for your native client application to make use of any of the supported flows that involves sending the user (via the web browser or a web view) to Azure AD to authenticate. This ensures that the user experience in all the scenarios above is correctly handled (consent prompt, change password prompt, multi-factor auth prompt, etc.). It will also allow you to make use of a token cache (improving the overall experience, since you're not adding a token request for every call to the backend).
Once the user has authenticated, your app will have a access token, a refresh token, and an ID token. You can use these tokens directly against your application backend. If your app's backend needs to make calls to other APIs in the context of the user, it can do so by exchanging the access token it got from the native client app for an access token to another resource, on behalf of the user to whom the access token was originally granted (in fact, there's a sample that does exactly this: active-directory-dotnet-webapi-onbehalfof).
For an Angular2 app, I recommend taking a look at the ADAL for JavaScript library, in combination with some of the wrapper libraries that have appeared to add support for Angular2 (e.g. angular2-adal).