Validate a token signature for subsequent request in a restful web api - security

When the user is authenticated I put a signed token in the response authorization header.
Every furthere access on a ressource url is only allowed with a valid signed token.
When I create the token and valdiate it:
var principal = tokenHandler.ValidateToken(tokenString, validationParameters);
then I get the principal (user who made the request) when the signed key is the same which got
used by creating the token.
That I can use the same signed key after authentication and during the ressource request to validate the token I have created this class:
public static class ApiConstants
{
private static readonly RNGCryptoServiceProvider CryptoProvider = new RNGCryptoServiceProvider(new byte[33]);
private static byte[] key = new byte[32];
static ApiConstants()
{
CryptoProvider.GetBytes(key);
}
public static byte[] GetSignedKey()
{
return key;
}
}
Is there anything wrong that I put this code in a static class which is actually my full purpose as I want the filling up of the byte array with random numbers to happen only one time!?
Is there still something I can improve?

You Can't make the signature token as static. because then it will become as global variable and shared by all request(thread). Also you will face concurrency issue with static field.
If you want to make it session specific. then you need to store in a session variable not in a static field.

Related

Azure Rest API usage with SAS Service

I am trying to use the Azure REST API with SAS Service. While I got this working with the most basic GET command, I am having trouble with the setup as soon as I need to add variables to the call as they are added at the same place as the SAS token. e.g. for "List Containers" I should use the URL "https://myaccount.blob.core.windows.net/?comp=list". But the "?comp=list" part is that the same place as the SAS Token. How can I give the request both the tokens and the variables? (I do not have much experience with REST APIs, so maybe I am misunderstanding something). I also posted my code below.
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
namespace ConsoleProgram
{
public class DataObject
{
public string Name { get; set; }
}
public class Class1
{
private const string URL = "url";
private static string urlParameters = "?token";
static void Main(string[] args)
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(URL);
// Add an Accept header for JSON format.
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
// List data response.
HttpResponseMessage response = client.GetAsync(urlParameters+ "&comp=list").Result; // Blocking call! Program will wait here until a response is received or a timeout occurs.
if (response.IsSuccessStatusCode)
{
Console.WriteLine(response.ToString());
// Parse the response body.
var dataObjects = response.Content.ReadAsStringAsync().Result; //Make sure to add a reference to System.Net.Http.Formatting.dll
Console.WriteLine("{0}", dataObjects);
}
else
{
Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
}
// Make any other calls using HttpClient here.
// Dispose once all HttpClient calls are complete. This is not necessary if the containing object will be disposed of; for example in this case the HttpClient instance will be disposed automatically when the application terminates so the following call is superfluous.
client.Dispose();
}
}
}
When we use the SAS token to call Azure blob rest API, the SAS token is used as the query string. So we can use '&' to splice SAS token and other query parameters, such as https://myaccount.blob.core.windows.net/?comp=list&{sasToken}.
Besides, please note that if you want to list containers in one storage account, you need to create an account SAS token. The service SAS token cannot implement it. Regarding how to create the account SAS token, please refer to here

Invalid Signature when generate bearer token

I am new to OAuth and I used this tutorial to generate access token from client app to target app. The code itself is working fine, but the access token I generated has invalid signature when I decoded on https://jwt.io/
Here's the code from the tutorial
public class ServicePrincipal
{
/// <summary>
/// The variables below are standard Azure AD terms from our various samples
/// We set these in the Azure Portal for this app for security and to make it easy to change (you can reuse this code in other apps this way)
/// You can name each of these what you want as long as you keep all of this straight
/// </summary>
static string authority = ""; // the AD Authority used for login. For example: https://login.microsoftonline.com/myadnamehere.onmicrosoft.com
static string clientId = ""; // client app's client id
static string clientSecret = ""; // client app's secret key
static string resource = ""; // target app's App ID URL
/// <summary>
/// wrapper that passes the above variables
/// </summary>
/// <returns></returns>
static public async Task<AuthenticationResult> GetS2SAccessTokenForProdMSAAsync()
{
return await GetS2SAccessToken(authority, resource, clientId, clientSecret);
}
static async Task<AuthenticationResult> GetS2SAccessToken(string authority, string resource, string clientId, string clientSecret)
{
var clientCredential = new ClientCredential(clientId, clientSecret);
AuthenticationContext context = new AuthenticationContext(authority, false);
AuthenticationResult authenticationResult = await context.AcquireTokenAsync(
resource, // the resource (app) we are going to access with the token
clientCredential); // the client credentials
return authenticationResult;
}
}
There is another piece of code I found that can also generate the access token:
AuthenticationContext authenticationContext =
new AuthenticationContext({authority});
ClientCredential clientCredential = new ClientCredential({client app id}, {client app secret});
try
{
AuthenticationResult result =
await authenticationContext.AcquireTokenAsync({target app's App ID URL},
clientCredential);
}
catch (Exception e)
{
return false;
}
Both of the code gave me invalid signature access token with version 1.0
There are two issues here:
I noticed is that when I decode the access token, it shows "ver": "1.0". Does it mean it is using OAuth1.0? Because I suppose to use OAuth 2.0.. Why would the code generate token that create OAuth1.0 not OAuth2.0?
Why would it be invalid signature?
I tried the same code with yours, got the same situation invalid signature, but when I changed the jwt ALGORITHM to HS256, I got the Signature Verified.
And the signature:
And the differences about RRS256 and HS256:
RS256 (RSA Signature with SHA-256) is an asymmetric algorithm, and it uses a public/private key pair: the identity provider has a private (secret) key used to generate the signature, and the consumer of the JWT gets a public key to validate the signature. Since the public key, as opposed to the private key, doesn't need to be kept secured, most identity providers make it easily available for consumers to obtain and use (usually through a metadata URL).
HS256 (HMAC with SHA-256), on the other hand, is a symmetric algorithm, with only one (secret) key that is shared between the two parties. Since the same key is used both to generate the signature and to validate it, care must be taken to ensure that the key is not compromised.
Are you posting your key into the form at jwt.io? Try to make a real rest call using the token in the authorization header. If everything is working and jwt isn't, maybe it's on them.
I noticed is that when I decode the access token, it shows "ver": "1.0". Does it mean it is using OAuth1.0? Because I suppose to use OAuth 2.0.. Why would the code generate token that create OAuth1.0 not OAuth2.0?
You are using OAuth2.0 , the ver:"1.0" means the JWT token is issued by Azure AD V1.0 Endpoint .
Why would it be invalid signature?
The API needs to check if the algorithm, as specified by the JWT header (property alg), matches the one expected by the API . AAD use RS256 , so you should change to RS256:
The normal way is to build from modulus and exponent , finding them from https://login.microsoftonline.com/common/discovery/keys matching kid and x5t from the token . Any use online tool like https://play.golang.org/ to get public key consists of two components: n and e. You can also use x5c value , click here for samples .

The provided anti-forgery token was meant for a different claims-based user than the current user - Token Authentication

my current Authentication process looks like this. I have a Auth API that generates a token with UseOAuthBearerAuthentication. Once I generate the token inside GrantResourceOwnerCredentials I set the Identity.Name by identity.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, context.UserName));
I want to store my token in a HttpOnly cookie, so in TokenEndpointResponse(OAuthTokenEndpointResponseContext context)
I saved it to a cookie, but I also need to prevent XSRF so I generate a XSRF token there as well.
string cookieToken, formToken;
AntiForgery.GetTokens(null, out cookieToken, out formToken);
If I look at the current Identity context from OAuthTokenEndpointResponseContext context, Identity.Name is set, so it should be using this name for the XSRF token generation. But HttpContext.Current.User.Identity is null In my both my APIs I also set
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimsIdentity.DefaultNameClaimType;
Now In my Resource API, I've already taken care of reading the auth token from the cookie and setting it to the Authorization header. That works fine. I have then created my own XSRF Attribute the verify the XSRF token.
public class ValidateAntiForgery : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
try
{
string cookieToken = "";
string formToken = "";
IEnumerable<string> tokenHeaders;
if (actionContext.Request.Headers.TryGetValues("X-XSRF-TOKEN", out tokenHeaders))
{
string[] tokens = tokenHeaders.First().Split(':');
if (tokens.Length == 2)
{
cookieToken = tokens[0].Trim();
formToken = tokens[1].Trim();
}
}
AntiForgery.Validate(cookieToken, formToken);
}
catch(Exception e)
{
actionContext.Response = actionContext.ControllerContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Bad Request");
}
}
}
Checking the Identity in actionContext, everything is set and the user is authenticated. However, AntiForgery.Validate(cookieToken, formToken); throws an exception as shown below. I've looked at the many other examples, but I can't find a solution.
System.Web.Mvc.HttpAntiForgeryException: 'The provided anti-forgery
token was meant for a different claims-based user than the current
user.'
EDIT: So it seems where I generate my XSRF token in TokenEndpointResponse(OAuthTokenEndpointResponseContext context)
context.Identity is set with the authenticated User, however HttpContext.Current.User is null. Even though the XSRF tokens generated here are technically valid, I think they are using null from the HttpContext. If I generate the XSRF token is a separate GET request with [Authorize] so that HttpContext.Current.User is not null, then the AntiForgery.Validate works fine.
I want the XSRF token to be returned with the Authentication token, but I'm not sure how to do that. How do I set HttpContext.Current.User.Identity?
EDIT 2: So I was able to fix the problem using a hacky way. When I want to generate the XSRF token I call the following function.
[Authorize]
public string generateXSRFToken(ClaimsIdentity identity)
{
HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(identity, new string[0]);
string cookieToken, formToken;
AntiForgery.GetTokens(null, out cookieToken, out formToken);
return cookieToken + ":" + formToken;
}
It works!, but it's ugly, I would like a more elegant way.
This should fix your problem:
var returnUrl = Request.QueryString["ReturnUrl"];
if (returnUrl.IsEmpty()) {
// Some external login providers always require a return URL value
returnUrl = Href("~/");
}
if (WebSecurity.Login(email, password, rememberMe))
{
Context.RedirectLocal(returnUrl);
return;
}
else
{
ModelState.AddFormError("The user name or password provided is incorrect.");
}

Azure MSAL JS: How to edit profile?

I've successfully implemented MSAL JS for Azure AD B2C.
The next step is to let the user edit their profile. I've created a new policy for Edit Profile.
But how to redirect the user there? There are only login methods / acquire token methods.
I've tried to set the authority to a different policy. It then does redirect to the right page, but then it starts complaining about errors in scopes, and it messes up the token locally.
editProfile() {
this.userAgentApp.authority = this.policyEditProfile;
this.userAgentApp.loginRedirect();
}
The ASP.NET code examples explicitly have an option to set the editProfile Policy ID: https://learn.microsoft.com/en-gb/azure/active-directory-b2c/active-directory-b2c-devquickstarts-web-dotnet-susi#update-code-to-use-your-tenant-and-policies
Feels like this is missing from MSAL.JS and I have to manually craft the URL, is that correct?
Yes, this is correct. You will need to use a different authority which URL is composed of the tenant and the policy name, as shown here:
private static string Tenant = "yourTenant.onmicrosoft.com";
public static string PolicySignUpSignIn = "b2c_1_susi";
public static string PolicyEditProfile = "b2c_1_edit_profile";
private static string BaseAuthority = "https://login.microsoftonline.com/tfp/{tenant}/{policy}/oauth2/v2.0/authorize";
public static string Authority = BaseAuthority.Replace("{tenant}", Tenant).Replace("{policy}", PolicySignUpSignIn);
public static string AuthorityEditProfile = BaseAuthority.Replace("{tenant}", Tenant).Replace("{policy}", PolicyEditProfile);
BTW, that sample, although for .NET Desktop shows how to use the edit profile and password reset policies: active-directory-b2c-dotnet-desktop , see in particular the EditProfileButton_Click method, the factor of acquiring the token (interactively) will trigger the dialog to edit the profile:
AuthenticationResult authResult = await App.PublicClientApp.AcquireTokenAsync(App.ApiScopes, GetUserByPolicy(App.PublicClientApp.Users, App.PolicyEditProfile), UIBehavior.SelectAccount, string.Empty, null, App.AuthorityEditProfile);

oauth/check_token does not check for roles/scopes associated with endpoint

I have one Authorization server and one resource server. I am creating access token at authorization server and try to use it at Resource server using RemoteTokenServices in oauth2 which hits '/oauth/check_token' internally to authorization server, where it only checks for token existence and its expiry. But it does not check for roles/scopes against endpoint given vs roles/scopes against access_token.
#FrameworkEndpoint
public class CheckTokenEndpoint {
#RequestMapping(value = "/oauth/check_token")
#ResponseBody
public Map<String, ?> checkToken(#RequestParam("token") String value) {
OAuth2AccessToken token = resourceServerTokenServices.readAccessToken(value);
if (token == null) {
throw new InvalidTokenException("Token was not recognised");
}
if (token.isExpired()) {
throw new InvalidTokenException("Token has expired");
}
OAuth2Authentication authentication = resourceServerTokenServices.loadAuthentication(token.getValue());
Map<String, ?> response = accessTokenConverter.convertAccessToken(token, authentication);
return response;
}
}
Above code snippet is from CheckTokenEndpoint.java.
Is there any way to achieve roles/scopes based authorization also?
If anyone else come across a similar issue with JWT token implementation using XML-based configuration, I have solved it the following way
Oh and my detailed post on how to implement Spring OAuth2 using XML-based configuration can be found here
Some assumptions
You are using JWT tokens that have custom claims
You have provided a custom implementation of JwtAccessTokenConvertor which in turn implements TokenEnhancer interface (feel free to implement AccessTokenConvertor & TokenEnhancer interfaces without having to use JwtAccessTokenConvertor)
You are using XML-based configuration
A closer look at the CheckTokenEndpoint source code reveals the follow
private AccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
And looking at the source code of DefaultAccessTokenConvertor, it is the default implementation of AccessTokenConvertor interface which basically have the following contracts
Map<String, ?> convertAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication);
OAuth2AccessToken extractAccessToken(String value, Map<String, ?> map);
OAuth2Authentication extractAuthentication(Map<String, ?> map);
In my case, I used JWT tokens, meaning that the token value that I pass to the /oauth/token_check endpoint is a signed (with RSA keypair) JWT and the TokenCheckEndpoint will do a few checks such as
Checks if the token is in the db (oauth_access_token table), this does not apply to JWT implementation as they are not necessarily stored in db
Check that its valid JWT token in the first place
Check that the signature of the token is correct and it has not be tampered
Check that its not expired
Other checks that I don't know of
In addition to the above, I needed to check that the custom claim such as scope (i.e. basically role and its associated permissions) is same in the database (making sure that roles didn't change since token was issued).
Based on my debugging, when the /oauth/check_token endpoint is hit, the extractAccessToken followed by extractAuthentication methods is called respectively (at least with JWT implementation).
Since I have extended JwtAccessTokenConvertor (which in turn implements AccessTokenConvertor & TokenEnhancer interfaces) in order to enhance my JWT token to add custom claims (i.e. scope) to it by overriding the enhance method as shown below
#Component
public class MyJwtAccessTokenConvertor extends JwtAccessTokenConverter {
#Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
DefaultOAuth2AccessToken result = new DefaultOAuth2AccessToken(accessToken);
//enhance the token with custom claims (i.e. user role scope)
//then return it
return result;
}
#Override
public OAuth2AccessToken extractAccessToken(String value, Map<String, ?> map) {
OAuth2AccessToken mytoken = tokenConverter.extractAccessToken(value, map);
/* validate the custom claims of token i.e. user role scopes
* and if any issue throw an exception
*/
return token;
}
}
I could easily validate that the JWT access token has the required user role scopes in the extractAccessToken method. If I detect any violation then I throw InvalidTokenException (can be custom exception too).

Resources