ASP.net web api refresh JWT logic question - asp.net-web-api

I am authenticating the client via JWT token. First of all, client sends a request to method A with its credentials. If client is valid, JWT token is send to the client. This token is valid for just 2 minutes. The client should send this token and some other data to method B in order to complete the process. Now there is a change in the design. I have to add another method for cancel/confirm. Client should call this new method to finish the transaction. But in order to call this new method, the client should use JWT token. I wonder if the client can use the JWT token which is used in method B if it is not expired. OR is there a way to reuse the same token? Is this possible? Or what is the preferred or best practice in this situation?
I am using asp.net web api2 and creating JWT token as follows and it works OK.
Here is the handler:
internal class TokenValidationHandler : DelegatingHandler
{
private static bool TryRetrieveToken(HttpRequestMessage request, out string token)
{
token = null;
IEnumerable<string> authzHeaders;
if (!request.Headers.TryGetValues("Authorization", out authzHeaders) || authzHeaders.Count() > 1)
{
return false;
}
var bearerToken = authzHeaders.ElementAt(0);
token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
return true;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpStatusCode statusCode;
string token;
//determine whether a jwt exists or not
if (!TryRetrieveToken(request, out token))
{
statusCode = HttpStatusCode.Unauthorized;
//allow requests with no token - whether a action method needs an authentication can be set with the claimsauthorization attribute
return base.SendAsync(request, cancellationToken);
}
try
{
const string sec = "zdRhpFvSSjdG9n7";
var now = DateTime.UtcNow;
var securityKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(sec));
SecurityToken securityToken;
JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
TokenValidationParameters validationParameters = new TokenValidationParameters()
{
ValidAudience = "http://111.111.111.111:1907/api/v3/token",
ValidIssuer = "http://111.111.111.111:1907/api/v3/token",
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
LifetimeValidator = this.LifetimeValidator,
IssuerSigningKey = securityKey
};
//extract and assign the user of the jwt
Thread.CurrentPrincipal = handler.ValidateToken(token, validationParameters, out securityToken);
HttpContext.Current.User = handler.ValidateToken(token, validationParameters, out securityToken);
return base.SendAsync(request, cancellationToken);
}
catch (SecurityTokenValidationException e)
{
statusCode = HttpStatusCode.Unauthorized;
}
catch (Exception ex)
{
statusCode = HttpStatusCode.InternalServerError;
}
return Task<HttpResponseMessage>.Factory.StartNew(() => new HttpResponseMessage(statusCode) { });
}
public bool LifetimeValidator(DateTime? notBefore, DateTime? expires, SecurityToken securityToken, TokenValidationParameters validationParameters)
{
if (expires != null)
{
if (DateTime.UtcNow < expires) return true;
}
return false;
}
}
Here is the token creation in the controller:
public static string createToken(string username)
{
//Set issued at date
DateTime issuedAt = DateTime.UtcNow;
//set the time when it expires
DateTime expires = DateTime.UtcNow.AddMinutes(2);
var tokenHandler = new JwtSecurityTokenHandler();
//create a identity and add claims to the user which we want to log in
ClaimsIdentity claimsIdentity = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Name, username)
});
const string sec = "zdRhpFvSSjdG9n7";
var now = DateTime.UtcNow;
var securityKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(sec));
var signingCredentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(securityKey, Microsoft.IdentityModel.Tokens.SecurityAlgorithms.HmacSha256Signature);
//create the jwt
var token =
(JwtSecurityToken)
tokenHandler.CreateJwtSecurityToken(issuer: "http://111.111.111.111:1907/api/v3/token", audience: "http://111.111.111.111:1907/api/v3/token",
subject: claimsIdentity, notBefore: issuedAt, expires: expires, signingCredentials: signingCredentials);
var tokenString = tokenHandler.WriteToken(token);
return tokenString;
}
How can I implement a refresh token mechanism in my code? Would you please help?
should I need refresh token in my scneario?
Here is my scenario:
In my database there is a users table which has user id, user name, hash, salt and password.
Client sends request to method A with user id and password which is provided to them
User id and password is checked if it is valid, access token created and send in response.
Client sends request to method B in order to complete the process with the given access token.
Now there is an other method (cancel-confim) comes into play.
In order to call this new method directly, the access token should not be expired right?
If access token is expired then client has to make a new request for access token and if the client is valid a brand new access token is provided. Since the resouce and aut server is the same, I think I don't need for refresh token mechanism right? I couldn't see any benefit of refresh token in my senario.
Please share your ideas?

Related

how to validate azure active directory token in webapi

I am trying to validate azure active directory token got in angularapp
i used following code in web api
[HttpGet]
[Route("Validate")]
public JwtSecurityToken Validate(string token)
{
string stsDiscoveryEndpoint = "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration";
ConfigurationManager<OpenIdConnectConfiguration> configManager = new ConfigurationManager<OpenIdConnectConfiguration>(stsDiscoveryEndpoint, new OpenIdConnectConfigurationRetriever());
OpenIdConnectConfiguration config = configManager.GetConfigurationAsync().Result;
TokenValidationParameters validationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidateIssuer = false,
IssuerSigningTokens = config.SigningTokens,
ValidateLifetime = false
};
JwtSecurityTokenHandler tokendHandler = new JwtSecurityTokenHandler();
SecurityToken jwt;
var result = tokendHandler.ValidateToken(token, validationParameters, out jwt);
return jwt as JwtSecurityToken;
}
the code show error at IssuerSigningTokens = config.SigningTokens, with the message
TokenValidationParameters does not contain definition for
IssuerSigningTokens
Can any one please provide me the solution?
Also i want to return my own token from validate method.
Today,we can use jwtSecurityTokenHandler with System.IdentityModel.Tokens.Jwt.
ATTENTION
System.IdentityModel.Tokens.Jwt version 5.x.x requires .NET Framework 5.x.
If the target framework is .NET Framework 4.5.x or 4.6.x take latest stable 4.x.x version of System.IdentityModel.Tokens.Jwt package.
Validate JWT
public override async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
...
// validate JWT
try
{
await JwtValidator.ValidateJwtToken(token, cancellationToken);
}
catch (SecurityTokenException e)
{
var validationSucceeded = false;
Contract.Assert(validationSucceeded, $"{ErrorCode.UNAUTHORIZED}JWT validation failed ({e.Message}).");
}
...
}
public class JwtValidator
{
private const string STS_DISCOVERY_ENDPOINT_SUFFIX = ".well-known/openid-configuration";
private const string URI_DELIMITER = "/";
public static async Task<SecurityToken> ValidateJwtToken(string token, CancellationToken cancellationToken)
{
var aadInstance = "https://login.microsoftonline.com/{0}";
var tenant = "example.com";
var audience = "853fb202-4201-4e20-97ae-4d5840d9490f";
var authority = string.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
// Fetch configuration
var stsDiscoveryEndpoint = string.Concat(authority, URI_DELIMITER, STS_DISCOVERY_ENDPOINT_SUFFIX);
ConfigurationManager<OpenIdConnectConfiguration> configManager = new ConfigurationManager<OpenIdConnectConfiguration>(stsDiscoveryEndpoint);
var config = await configManager.GetConfigurationAsync(cancellationToken);
// extract issuer and token for validation
var issuer = config.Issuer;
var signingTokens = config.SigningTokens.ToList();
// validate token
var validationParameters = CreateTokenValidationParameters(signingTokens, issuer, audience);
var jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
SecurityToken jwt;
jwtSecurityTokenHandler.ValidateToken(token, validationParameters, out jwt);
return jwt;
}
private static TokenValidationParameters CreateTokenValidationParameters(List<SecurityToken> signingTokens, string issuer, string audience)
{
Contract.Requires(null != signingTokens);
Contract.Requires(!string.IsNullOrWhiteSpace(issuer));
return new TokenValidationParameters()
{
ValidAudience = audience,
ValidIssuer = issuer,
IssuerSigningTokens = signingTokens,
CertificateValidator = X509CertificateValidator.None,
ValidateLifetime = true
};
}
}
Also i want to return my own token from validate method.
When using OpenIdConnectConfiguration configManager.GetConfigurationAsync(cancellationToken); has to be called in async context by using await (Implies that the surrounding method has to be async).
Otherwise the method call GetConfigurationAsync(cancellationToken) did never return. Even when I tried to run the asynchronous method synchronously by calling .Result or using other mechanisms to run ansynchronous methods synchronous the method didn’t return.
You can see more details about Manual JWT Validation against Azure Active Directory in this blog.

How to store additional data per user like session on Owin using Bearer Token

I need to store a token for thirdy party software calls on my controller after my client sign in, so I tried to save this on User Claims:
public class BaseController : ApiController
{
private const string Token = "thirdyparty.token";
private string Token
{
set
{
// Here I want to store a token in any way (Session, Cache, etc)
var claimsIdentity = (ClaimsIdentity)User.Identity;
var claims = claimsIdentity.Claims;
var tokenClaim = claims.FirstOrDefault(x => x.Type == Token);
if (Token != null)
{
claimsIdentity.RemoveClaim(tokenClaim);
}
claimsIdentity.AddClaim(new Claim(Token, value));
}
get
{
// Here I want to get the token
var claimsIdentity = (ClaimsIdentity)User.Identity;
var claims = claimsIdentity.Claims;
var tokenClaim = claims.FirstOrDefault(x => x.Type == Token);
return tokenClaim?.Value;
}
}
}
This did not work, my new Claim disappeared every time a new request is made.
So, how can I store some additional information per user?
The problem is that the claims are part of the bearer token.
So even if you add the claim to the current identity the next request will
have the old claim values as they are part of the token sent with the new request.
So, if you add a claim you need to generate a new token as well and return that to the client.
One way to generate a new token is to store the OAuthAuthorizationServerOptions, used in the
Startup.cs class, as a static variable and then use that where it's needed
public class Startup
{
public static OAuthAuthorizationServerOptions OAuthServerOptions { get; private set; }
public void Configuration(IAppBuilder app)
{
ConfigureOAuth(app);
//....add the rest
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new AuthProvider() //Your derived OAuthAuthorizationServerProvider
};
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
Then to generate a new token
var claimsIdentity = ... //The claim identity after you have added the new claims
var newToken = Startup.OAuthServerOptions.AccessTokenFormat.Protect(new AuthenticationTicket(claimsIdentity, new AuthenticationProperties()));

Why Context.User.Identity.Name is empty from Xamarin?

I can't figure out why when I try to connect from Xamarin Context.User.Indetity.Name is empty. Is there anything special I need to do? I logged in to the server and the user has a connection stablished. After that I use the following code:
var Connection = new HubConnection(Url);
_hub = Connection.CreateHubProxy(hubName);
_hub.On(srvEvent, onData);
await Connection.Start();
But I never get the username. What am I doing wrong?
Here's the code for the server:
var name = Context.User.Identity.Name;
Connections.Add(name, Context.ConnectionId);
return base.OnConnected();
It works when it comes from the web app, not from the xamarin app.
Thanks!
Here is the code I was telling you about.
I'm using an external OAuth2 server for authentication, so I must pass the access token to SignalR somehow, because SignalR uses web sockets for the messages back and forth I can't pass the access token in the header because this is not supported by web sockets.
I'm passing that access token as a query string parameter this way (Javascript client)
$.connection.hub.qs = "access_token=" + mytoken;
Then on my SignalR I added a middleware that takes that query string and adds it to the header as an Authorization header using Bearer Token. This is done this way in my startup class
app.UseAuthQSTokenExtractor();
The code for the middleware is this one
namespace Owin
{
public static class AuthorizationQSTokenExtractorExtension
{
public static void UseAuthQSTokenExtractor(this IAppBuilder app)
{
app.Use<AuthorizationQsTokenExtractorMiddleware>();
}
}
}
namespace Chat.Middleware
{
public class AuthorizationQsTokenExtractorMiddleware : OwinMiddleware
{
public AuthorizationQsTokenExtractorMiddleware(OwinMiddleware next)
: base(next)
{
}
public override async Task Invoke(IOwinContext context)
{
Debug.WriteLine("signalr-auth-middleware");
string bearerToken = context.Request.Query.Get("access_token");
Debug.WriteLine("signar-bearer: " + bearerToken);
if (bearerToken != null)
{
TokenHelper.DecodeAndWrite(bearerToken);
string[] authorization = { "Bearer " + bearerToken };
context.Request.Headers.Add("Authorization", authorization);
}
await Next.Invoke(context);
}
}
My startup class then looks like this
app.UseCors(CorsOptions.AllowAll);
app.UseAuthQSTokenExtractor();
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
app.UseIdentityServerBearerTokenAuthentication(
new IdentityServerBearerTokenAuthenticationOptions
{
Authority = ConfigurationManager.AppSettings["api:idserver"],
RequiredScopes = new[]
{
"chat-hub"
}
});
var hubConfiguration = new HubConfiguration ();
hubConfiguration.EnableDetailedErrors = true;
app.RunSignalR(hubConfiguration);
You can see in the code above where I tell SignalR to use the Oauth2 Server, that code is this one
app.UseIdentityServerBearerTokenAuthentication(
new IdentityServerBearerTokenAuthenticationOptions
{
Authority = ConfigurationManager.AppSettings["api:idserver"],
RequiredScopes = new[]
{
"chat-hub"
}
});
After all this is set up I have access to my Context.User.Identity.Name and if you want to get the others IdentityClaim you can do this
var identity = Context.User.Identity as ClaimsIdentity;
Which I'm using that code above to get the subjectId (userid) like this
public static string[] GetIdentityClaimsIssSub(HubCallerContext Context)
{
var identity = Context.User.Identity as ClaimsIdentity;
if (identity == null)
return null;
var issuerFromIdentity = identity.FindFirst("iss");
var subFromIdentity = identity.FindFirst("sub");
if (issuerFromIdentity == null || subFromIdentity == null)
return null;
return new string[] { issuerFromIdentity.Value, subFromIdentity.Value };
}
I hope it helps

LinqToTwitter Search Never Returns

I am attempting to use LinqToTwitter to search twitter. It works fine as run in an NUnit test but it does not work with ASP.NET or as a WinForm app. I am not sure what Authorizer to use.
public async Task<Search> SearchTwitter(string searchWords)
{
var twitterCtx = BuildTwitterContext();
Task<Search> searchResponse = (from search in twitterCtx.Search
where search.Type == SearchType.Search &&
search.Query == searchWords
select search)
.SingleOrDefaultAsync();
return await searchResponse;
}
private static TwitterContext BuildTwitterContext()
{
IAuthorizer authorizer;
if (HttpContext.Current == null)
authorizer = new PinAuthorizer();
else
authorizer = new AspNetSignInAuthorizer();
InMemoryCredentialStore credentialStore = new InMemoryCredentialStore();
credentialStore.ConsumerKey = consumerKey;
credentialStore.ConsumerSecret = consumerSecret;
credentialStore.OAuthToken = accessToken;
credentialStore.OAuthTokenSecret = accessTokenSecret;
authorizer.CredentialStore = credentialStore;
var twitterCtx = new TwitterContext(authorizer);
return twitterCtx;
}
ASP.NET is different because of the page redirections where you start the authorization and then finish after Twitter redirects back. Here's the LINQ to Twitter documentation that will explain how OAuth works and give you a better idea on which authorizers to use:
https://github.com/JoeMayo/LinqToTwitter/wiki/Learning-to-use-OAuth
The L2T source code also has demos. Here's an OAuth controller demo:
https://github.com/JoeMayo/LinqToTwitter/blob/master/New/Linq2TwitterDemos_Mvc/Controllers/OAuthController.cs
public class OAuthController : AsyncController
{
public ActionResult Index()
{
return View();
}
public async Task<ActionResult> BeginAsync()
{
//var auth = new MvcSignInAuthorizer
var auth = new MvcAuthorizer
{
CredentialStore = new SessionStateCredentialStore
{
ConsumerKey = ConfigurationManager.AppSettings["consumerKey"],
ConsumerSecret = ConfigurationManager.AppSettings["consumerSecret"]
}
};
string twitterCallbackUrl = Request.Url.ToString().Replace("Begin", "Complete");
return await auth.BeginAuthorizationAsync(new Uri(twitterCallbackUrl));
}
public async Task<ActionResult> CompleteAsync()
{
var auth = new MvcAuthorizer
{
CredentialStore = new SessionStateCredentialStore()
};
await auth.CompleteAuthorizeAsync(Request.Url);
// This is how you access credentials after authorization.
// The oauthToken and oauthTokenSecret do not expire.
// You can use the userID to associate the credentials with the user.
// You can save credentials any way you want - database,
// isolated storage, etc. - it's up to you.
// You can retrieve and load all 4 credentials on subsequent
// queries to avoid the need to re-authorize.
// When you've loaded all 4 credentials, LINQ to Twitter will let
// you make queries without re-authorizing.
//
//var credentials = auth.CredentialStore;
//string oauthToken = credentials.OAuthToken;
//string oauthTokenSecret = credentials.OAuthTokenSecret;
//string screenName = credentials.ScreenName;
//ulong userID = credentials.UserID;
//
return RedirectToAction("Index", "Home");
}
}
Notice that it uses a WebAuthorizer/SessionStateCredentials pair and separates the start of authorization with a separate action method (specified via callback) for completion.
The following demo shows how to perform OAuth in a WinForms app:
https://github.com/JoeMayo/LinqToTwitter/blob/master/New/Demos/Linq2TwitterDemos_WindowsForms/OAuthForm.cs
public partial class OAuthForm : Form
{
PinAuthorizer pinAuth = new PinAuthorizer();
public OAuthForm()
{
InitializeComponent();
}
async void OAuthForm_Load(object sender, EventArgs e)
{
pinAuth = new PinAuthorizer
{
// Get the ConsumerKey and ConsumerSecret for your app and load them here.
CredentialStore = new InMemoryCredentialStore
{
ConsumerKey = ConfigurationManager.AppSettings["consumerKey"],
ConsumerSecret = ConfigurationManager.AppSettings["consumerSecret"]
},
// Note: GetPin isn't used here because we've broken the authorization
// process into two parts: begin and complete
GoToTwitterAuthorization = pageLink =>
OAuthWebBrowser.Navigate(new Uri(pageLink, UriKind.Absolute))
};
await pinAuth.BeginAuthorizeAsync();
}
async void SubmitPinButton_Click(object sender, EventArgs e)
{
await pinAuth.CompleteAuthorizeAsync(PinTextBox.Text);
SharedState.Authorizer = pinAuth;
// This is how you access credentials after authorization.
// The oauthToken and oauthTokenSecret do not expire.
// You can use the userID to associate the credentials with the user.
// You can save credentials any way you want - database, isolated storage, etc. - it's up to you.
// You can retrieve and load all 4 credentials on subsequent queries to avoid the need to re-authorize.
// When you've loaded all 4 credentials, LINQ to Twitter will let you make queries without re-authorizing.
//
//var credentials = pinAuth.CredentialStore;
//string oauthToken = credentials.OAuthToken;
//string oauthTokenSecret = credentials.OAuthTokenSecret;
//string screenName = credentials.ScreenName;
//ulong userID = credentials.UserID;
//
Close();
}
}
In this case, you can use a PinAuthorizer with an InMemoryCredentialStore. If you look at that demo, it uses a Web Browser control to navigate to Twitter and manage the OAuth flow.
Look at the URL above for the Learning to use OAuth for examples of other IAuthorizer derived types that you can use in different scenarios. Also, download the source code and step through with the debugger to get a feel for the OAuth workflow.

Json web token in Web api not validating the refresh token after its expiry

I am using JWT authentication for WEB API using OAuth 2. I am using Refresh tokens mechanism. I am able to generate the refresh token and call API service from it before the expiration time. Once the token is expired , i am calling service to issue new token using refresh token id. But its giving error in my CustomJWTFormat class UnProtect method as it not implement any logic. I am not getting what logic to be implemented to reissue JWT refresh token.
Sample codes for configuring serviec to use JSON web token format:
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(5),
Provider = new SimpleAuthorizationServerProvider(),
RefreshTokenProvider = new SimpleRefreshTokenProvider(),
AccessTokenFormat = new CustomJwtFormat(<issuer>),
RefreshTokenFormat = new CustomJwtFormat(<issuer>)
};
Sample code of my CustomJWTFormat class:
public class CustomJwtFormat : ISecureDataFormat<AuthenticationTicket>
{
private const string AudiencePropertyKey = "as:client_id";
private readonly string _issuer = string.Empty;
private string symmetricKeyAsBase64 = string.Empty;
public CustomJwtFormat(string issuer)
{
_issuer = issuer;
}
public string Protect(AuthenticationTicket data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
string audienceId = data.Properties.Dictionary.ContainsKey(AudiencePropertyKey) ? data.Properties.Dictionary[AudiencePropertyKey] : null;
if (string.IsNullOrWhiteSpace(audienceId))
{
audienceId = <audience>;
symmetricKeyAsBase64 = <secret key>;
}
else
{
using (AuthRepository _repo = new AuthRepository())
{
var audience = _repo.FindClient(audienceId);
symmetricKeyAsBase64 = audience.Secret;
}
}
var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);
var signingKey = new HmacSigningCredentials(keyByteArray);
var issued = data.Properties.IssuedUtc;
var expires = data.Properties.ExpiresUtc;
var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingKey);
var handler = new JwtSecurityTokenHandler();
var jwt = handler.WriteToken(token);
return jwt;
}
///Need logic for this method. Its calling when service is called to generated new token for refresh id
public AuthenticationTicket Unprotect(string protectedText)
{
throw NotImplementedException();
}
}
}
Any help will be appreciated.
Have a look at this sample to give you an idea of validating a token.
https://github.com/AzureADSamples/WebAPI-ManuallyValidateJwt-DotNet/blob/master/TodoListService-ManualJwt
In particular, Global.asax.cs.

Resources