How to handle user OIDC tokens in Blazor Server when the browser is refreshed and the cookie’s tokens are invalid? - oauth

Microsoft recommend against using HttpContext in Blazor Server (here). To work around the issue of how to pass user tokens to a Blazor Server app, Microsoft recommend storing the tokens in a Scoped service (here). Jon McGuire’s blog suggests a similar approach that stores the tokens in Cache (here).
Microsoft’s approach above works just fine as long as the user stays within the same Blazor Server connection. However if the access_token is refreshed and the user then reloads the page either by pressing F5 or by pasting a URL into the address bar, then an attempt is made to retrieve the tokens from the cookie. By this time, the access_token and refresh_token in the cookie are no longer valid. Jon McGuire mentions this problem at the end of his blog post and refers to it as Stale Cookies (here). He gives hints about a possible solution, but is very light on implementation instructions. There are many comments at the bottom of that post from people unable to implement a solution, with no apparent working solution suggested. I spent a lot of time searching for a solution and all I found were people asking for one and not receiving any answers that worked.
Having found a solution that seems to work well, and also seems fairly principled, I thought it might be worth sharing my solution here. I would welcome any constructive criticism or suggestions for any significant improvements.

Edit 20220715: After some feedback on our approach from Dominic Baier we removed our Scoped UserSubProvider service in favour of using AuthenticationStateProvider instead. This has simplified our approach. I have edited the following answer to reflect this change.
This approach combines advice from Microsoft on how to pass tokens to a Blazor Server app (here), with server side storage of tokens in a Singleton service for all users (inspired by Dominick Baier’s Blazor Server sample project on GitHub here).
Instead of capturing the tokens in the _Host.cshtml file and storing them in a Scoped service (like Microsoft do in their example), we use the OnTokenValidated event in a similar way to Dominick Baier’s sample, storing the tokens in a Singleton service that holds tokens for all Users, we call this service ServerSideTokenStore.
When we use our HttpClient to call an API and it needs an access_token (or refresh_token), then it retrieves the User’s sub from an injected AuthenticationStateProvider, uses it to call ServerSideTokenStore.GetTokensAsync(), which returns a UserTokenProvider (similar to Microsoft’s TokenProvider) containing the tokens. If the HttpClient needs to refresh the tokens then it populates a UserTokenProvider and saves it by calling ServerSideTokenStore.SetTokensAsync().
Another issue we had was if a separate instance of the web browser is open while the app restarts (and therefore loses the data held in ServerSideTokenStore) the user would still be authenticated using the cookie, but we’ve lost the access_token and refresh_token. This could happen in production if the application is restarted, but happens a lot more frequently in a dev environment. We work around this by handling OnValidatePrincipal and calling RejectPrincipal() if we cannot get a suitable access_token. This forces a round trip to IdentityServer which provides a new access_token and refresh_token. This approach came from this stack overflow thread.
(For clarity/focus, some of the code that follows excludes some standard error handling, logging, etc.)
Getting the User sub claim from AuthenticationStateProvider
Our HttpClient gets the user's sub claim from an injected AuthenticationStateProvider. It uses the userSub string when calling ServerSideTokenStore.GetTokensAsync() and ServerSideTokenStore.SetTokensAsync().
var state = await AuthenticationStateProvider.GetAuthenticationStateAsync();
string userSub = state.User.FindFirst("sub")?.Value;
UserTokenProvider
public class UserTokenProvider
{
public string AccessToken { get; set; }
public string RefreshToken { get; set; }
public DateTimeOffset Expiration { get; set; }
}
ServerSideTokenStore
public class ServerSideTokenStore
{
private readonly ConcurrentDictionary<string, UserTokenProvider> UserTokenProviders = new();
public Task ClearTokensAsync(string userSub)
{
UserTokenProviders.TryRemove(userSub, out _);
return Task.CompletedTask;
}
public Task<UserTokenProvider> GetTokensAsync(string userSub)
{
UserTokenProviders.TryGetValue(userSub, out var value);
return Task.FromResult(value);
}
public Task StoreTokensAsync(string userSub, UserTokenProvider userTokenProvider)
{
UserTokenProviders[userSub] = userTokenProvider;
Return Task.CompletedTask;
}
}
Startup.cs ConfigureServices (or equivalent location if using .NET 6 / whatever)
public void ConfigureServices(IServiceCollection services)
{
// …
services.AddAuthentication(…)
.AddCookie(“Cookies”, options =>
{
// …
options.Events.OnValidatePrincipal = async context =>
{
if (context.Principal.Identity.IsAuthenticated)
{
// get user sub
var userSub = context.Principal.FindFirst(“sub”).Value;
// get user's tokens from server side token store
var tokenStore =
context.HttpContext.RequestServices.GetRequiredService<IServerSideTokenStore>();
var tokens = await tokenStore.GetTokenAsync(userSub);
if (tokens?.AccessToken == null
|| tokens?.Expiration == null
|| tokens?.RefreshToken == null)
{
// if we lack either an access or refresh token,
// then reject the Principal (forcing a round trip to the id server)
context.RejectPrincipal();
return;
}
// if the access token has expired, attempt to refresh it
if (tokens.Expiration < DateTimeOffset.UtcNow)
{
// we have a custom API client that takes care of refreshing our tokens
// and storing them in ServerSideTokenStore, we call that here
// …
// check the tokens have been updated
var newTokens = await tokenStore.GetTokenAsync(userSubProvider.UserSub);
if (newTokens?.AccessToken == null
|| newTokens?.Expiration == null
|| newTokens.Expiration < DateTimeOffset.UtcNow)
{
// if we lack an access token or it was not successfully renewed,
// then reject the Principal (forcing a round trip to the id server)
context.RejectPrincipal();
return;
}
}
}
}
}
.AddOpenIdConnect(“oidc”, options =>
{
// …
options.Events.OnTokenValidated = async n =>
{
var svc = n.HttpContext.RequestServices.GetRequiredService<IServerSideTokenStore>();
var culture = new CultureInfo(“EN”) ;
var exp = DateTimeOffset
.UtcNow
.AddSeconds(double.Parse(n.TokenEndpointResponse !.ExpiresIn, culture));
var userTokenProvider = new UserTokenProvider()
{
AcessToken = n.TokenEndpointResponse.AccessToken,
Expiration = exp,
RefreshToken = n.TokenEndpointResponse.RefreshToken
}
await svc.StoreTokensAsync(n.Principal.FindFirst(“sub”).Value, userTokenProvider);
};
// …
});
// …
}

Related

ASP.NET Core Identity x Docker - Confirmation link invalid on other instances

I am currently developing a web API with ASP.NET Core, using Microsoft Identity Core as for the identity management. When a user registers, it is sent an email with a confirmation link - pretty basic so far.
The problem comes when publishing my API to Azure using a containerized Azure App Service, and when setting the number of instances to 2 or more. The confirmation link seems to be working only half the time; tests on my dev machine with multiple Docker containers running seemed to confirm that fact, as the confirmation link could be validated only on the instance the user had registered on (hence the instance where the confirmation link was created).
Having dug a bit on the subject by reading this article by Steve Gordon, and explored the public GitHub code for Identity Core, I still don't understand why different container instances would return different results when validating the token, as the validation should mainly be based on the user SecurityStamp (that remains unchanged between the instances becauses they all link to the same database).
Also, enabling 'debug' logging for the Microsoft.AspNetCore.Identity only logged
ValidateAsync failed: unhandled exception was thrown.
during token validation from the DataProtectorTokenProvider.ValidateAsync() method from AspNetCore.Identity, so it is not very helpful as I can't see precisely where the error happens...
May this be linked to the token DataProtector not being the same on different instances? Am I searching in the wrong direction? Any guess, solution or track for this?
Help would be immensely appreciated 🙏
Here is some simplified code context from my app for the record.
UserManager<User> _manager; // Set from DI
// ...
// Creating the user and sending the email confirmation link
[HttpGet(ApiRoutes.Users.Create)]
public async Task<IActionResult> RegisterUser(UserForRegistrationDto userDto)
{
var user = userDto.ToUser();
await _manager.CreateAsync(user, userDto.Password);
// Create the confirmation token
var token = await _manager.CreateEmailConfirmationTokenAsync(user);
// Generate the confirmation link pointing to the below 'ConfirmEmail' endpoint
var confirmationLink = Url.Action("ConfirmEmail", "Users",
new { user.Email, token }, Request.Scheme);
await SendConfirmationEmailAsync(user, confirmationLink); // Some email logic elsewhere
return Ok();
}
// Confirms the email using the passed token
[HttpGet(ApiRoutes.Users.ValidateEmail)]
public async Task<IActionResult> ConfirmEmail(string email, string token)
{
var user = await _userManager.FindByEmailAsync(email);
if (user == null)
{
return NotFound();
}
var result = await _userManager.ConfirmEmailAsync(user, token);
if (!result.Succeeded)
{
return BadRequest();
}
return Ok();
}
Token generated based on security stamp but Identity uses DataProtector to protect the token content. By default the data protection keys stored at location %LOCALAPPDATA%\ASP.NET\
If the application runs on single machine it is perfectly fine as there is no scope for key mismatch. But deployed on multiple instances the tokens will not work sometimes as the Keys are different on different machines and there is no guarantee the generation of token and validation of token will come to same instance.
To solve user redis or azurekeyvault
https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview?view=aspnetcore-6.0#persisting-keys-with-redis

Google SignIn - "access_token" vs "id_token" vs "code"

In our website we used to use access_token when logging people with Google Sign In. First, we redirect the user to google, user brings the access_token to us, and we validate that token to make sure the user is the actual Google user.
Then, we needed a Google sign-in feature for our Android app, so I wanted the Android developer to bring access_token to us. He replied he couldn't. I searched about that finding almost no documentation about access_token. In documentation, Google says me to use the "id_token".
OK, I wanted the developer to bring me the id_token, and I have successfully verified the token's integrity. Then I wanted to implement the same for websites.
My c# code is:
string googleId = GoogleJsonWebSignature.ValidateAsync(idToken).Result.Subject;
It worked when I ran it locally, but when I tried in production, it gave an error: JWT is not yet valid
Is id_token the correct way to send to the backend and verify? I found another option too: code.
Code is something like A/12112312......
Access_token is something like ya29.somemorestring
My question is, Which one is correct to send to the backend? By the way, I think access_token is sort of deprecated or something like that.
Yes, you should be using the id_token. You get the id_token on the client side using this:
var id_token = googleUser.getAuthResponse().id_token;
and validating it on the server side using (do in a try/catch block to catch any errors):
token = await GoogleJsonWebSignature.ValidateAsync(idToken);
The JWT is not yet valid error is due to the time on your server being slow. Even a few seconds slow will cause this problem. To be sure of this working all the time, you'll need to implement a custom clock which gets an accurate time from somewhere. Here's an example using NNTP:
public class AccurateClock : Google.Apis.Util.IClock
{
const int UpdateIntervalMinutes = 60;
const string NntpServer = "time.nist.gov";
private TimeSpan _timeOffset;
private DateTime _lastChecked;
public AccurateClock()
{
_timeOffset = TimeSpan.FromSeconds(0);
_lastChecked = DateTime.MinValue;
}
private DateTime GetTime()
{
try
{
if (DateTime.Now.Subtract(_lastChecked).TotalMinutes >= UpdateIntervalMinutes)
{
// Update offset
var client = new TcpClient(NntpServer, 13);
DateTime serverTime;
using (var streamReader = new StreamReader(client.GetStream()))
{
var response = streamReader.ReadToEnd();
var utcDateTimeString = response.Substring(7, 17);
serverTime = DateTime.ParseExact(utcDateTimeString, "yy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
}
_timeOffset = DateTime.UtcNow.Subtract(serverTime);
_lastChecked = DateTime.Now;
}
var accurateTime = DateTime.UtcNow.Subtract(_timeOffset);
return accurateTime;
}
catch (Exception ex)
{
return DateTime.UtcNow;
}
}
public DateTime Now
{
get
{
return GetTime().ToLocalTime();
}
}
public DateTime UtcNow
{
get
{
return GetTime();
}
}
}
You then pass the custom clock to the validation method.
token = await GoogleJsonWebSignature.ValidateAsync(idToken, new AccurateClock());
Please note: This will update the difference between the correct time and the local machine time every time the class is created, so you really want to register this as a Singleton in whatever IOC container you are using and pass the reference to the validator instead. It will then recheck the time using NNTP every hour. If you are not using an IOC Container you could make the class static.
id_token is a JWT token, that you validate and extract information such as "email", "name" etc. This is actually what you need in a regular case.
code and access_token are part of the flow when a user doesn't use your app in current moment but your app wants to make any actions behalf of them. Google calls it offline access https://developers.google.com/identity/sign-in/web/server-side-flow

allow mvc5 c# webapi so that only my app can access it

I am making the api calls through the controller action method as below. The following is the working code of it.
But I want to secure the webapi so that only my application can access it. I have seen sources with login credentials
but in my case it is a public facing website with no login users.
Only the calls from my application should access it. Could anyone please suggest what can be done. or Is my current code with ValidateReferrer is suffice to handle?
[HttpGet]
[ValidateReferrer]
[ActionName("GetFind")]
[CacheOutput(ClientTimeSpan = 300, ServerTimeSpan = 300)]
public ApiQueryResponse GetFind(string query)
{
return _Worker.GetFind(query);
}
Validate Referrer in the controller has the following implementation:
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext.HttpContext == null)
{
throw new System.Web.HttpException("No Http context, request not allowed.");
}
else
{
if (filterContext.HttpContext.Request.UrlReferrer == null)
{
throw new System.Web.HttpException("Referrer information missing, request not allowed.");
}
else if (filterContext.HttpContext.Request.UrlReferrer.Host != filterContext.HttpContext.Request.Url.Host)
{
throw new System.Web.HttpException(string.Format("Possible cross site request forgery attack, request sent from another site: {0}", filterContext.HttpContext.Request.UrlReferrer.Host));
}
}
}
In the worker class,
public ApiQueryResponse GetFind(string query)
{
var results = GetResults(Settings.ApiKey, SetFindParameters(query), Resource);
return results;
}
private ApiQueryResponse GetResults(string apiKey, string parameterQuery, string Resource)
{
var results = new ApiQueryResponse();
if (apiKey != null && !String.IsNullOrWhiteSpace(apiKey))
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = client.GetAsync(string.Format("{0}/{1}?{2}&key={3}", WebApiUrl, Resource, parameterQuery, apiKey)).Result;
if (response.IsSuccessStatusCode)
{
var responseBodyAsText = response.Content.ReadAsStringAsync().Result;
results = JsonConvert.DeserializeObject<ApiQueryResponse>(responseBodyAsText);
}
}
}
return results;
}
Again this is the case where you have to authenticate your "application" but not users. If you check facebook/twitter/gmail api's, they have a client secret and client id to authenticate the application. But still there will be an "Authorize" call made with this id and secret for which the api returns a token and this token is used henceforth to authorize the other requests. This token will also have an expiry and there are methods to get refresh tokens.
Thus said, you have to take a call on how much security you have to implement for your api's. You could have a similar approach where your client first asks for a security token by providing the client id and secret (which should really be a secret). You can check this id and secret against your store (may be database) and if that passes the validation, you can send back a token which you could authroize using [Authroize] attribute or by custom validation.
How to create tokens should be another discussion IMO. Simple approach is mentioned here for eg- how to generate a unique token which expires after 24 hours?. There are other standard ways of generating tokens JWT/OAuth tokens.
EDIT
As a simple approach (not taking much security aspects into consideration) would be:
Create an app secret (may be a Guid value)
While sending request, take current timestamp and encrypt (have your
own encrypt and decrypt logic) the timestamp with the app secret. Lets call that encrypted value as 'token'
Pass the token in your request header (may be a custom header,say,
x-my-auth)
In the api, have a custom authorize filter
In the custom filter, overridden OnAuthroizeCore method, get the
token from request header
Decrypt the token using the same app secret and you will get the
timestamp sent from the client
If decryption is fine, then we are through the first step/ or the
token passed the first step
Additionaly, check whether the difference between the current time
and the time decrypted from token is more than 5(*you can have your
own expiry value)
If the difference is more than your expiry limit, return false which
would throw unauthorized exception back to the client (do the same if the token fails to decrypt)
The expiry check is to handle the scenario where someone hacking your
token from the request and then using it afterwards. In case if he
uses the token after your expiry, this would throw unauthorized
Consider the above logic and entire description just as a "food for thought" and DO NOT use it without proper research and understanding. My idea was to give some basic idea about the application authentication until someone really good at this writes up a really nice article in this post

Using OAuth2 refresh tokens in an ASPMVC application

Scenario
I am using the OWIN cookie authentication middleware to protected my site as follows
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
ExpireTimeSpan = new TimeSpan(0, 20, 0),
SlidingExpiration = true
});
}
On login, I use the resource owner password flow to call my token service and retrieve both an access and refresh token.
I then add the refresh token, access token and the time the access token expires to my claims and then call the following to to persist this information to my authentication cookie.
HttpContext
.GetOwinContext()
.Authentication
.SignIn(claimsIdentityWithTokenAndExpiresAtClaim);
Then before calling any service, I can retrieve the access token from my current claims and associate it with the service call.
Problem
Before calling any service, I should really check if the access token has expired and if so use the refresh token to get a new one. Once I have a new access token, I can call the service, however I then need to persist a new authentication cookie with the new access token, refresh token and expiry time.
Is there any nice way to do this transparently to the caller of the service?
Attempted solutions
1) Check before calling every service
[Authorize]
public async Task<ActionResult> CallService(ClaimsIdentity claimsIdentity)
{
var accessToken = GetAccessToken();
var service = new Service(accessToken).DoSomething();
}
private string GetAccessToken(ClaimsIdentity claimsIdentity) {
if (claimsIdentity.HasAccessTokenExpired())
{
// call sts, get new tokens, create new identity with tokens
var newClaimsIdentity = ...
HttpContext
.GetOwinContext()
.Authentication
.SignIn(newClaimsIdentity);
return newClaimsIdentity;
} else {
return claimsIdentity.AccessToken();
}
}
This would work, but it's not sustainable. Also I could not longer use dependency injection to inject my services as the service needs the access token at call time and not construction time.
2) Use some kind of service factory
Before create the service with its access token, it would perform the refresh if needed. The issue it that I'm not sure how I can get the factory to return both a service and also set the cookie within the implementation in a nice way.
3) Do it in a action filter instead.
The thinking is that the session cookie has a 20 minutes sliding expiry. On ever page request, I can check if the access token is more than halfway through it's expiry (ie. if the access token has an expiry of an hour, check to see if it has less than 30 minutes to expiry). If so, perform the refresh. The services can rely on the access token not being expired. Lets say you hit the page just before the 30 minutes expiry and stayed on the page for 30 minutes, the assumption is the session timeout (20 minutes idle) will kick in before you call the service and you wil be logged off.
4) Do nothing and catch the exception from calling a service with an expired token
I couldn't figure out a nice way to get a new token and retry the service call again without having to worry about side effects etc. Plus it would be nicer to check for expiration first, rather than wait for the time it takes the service to fail.
Neither of these solutions are particularly elegant. How are others handling this?
Update:
I spent some time looking in to various options on how to implement this efficiently at the server side with your current setup.
There are multiple ways (like Custom-Middleware, AuthenticationFilter, AuthorizationFilter or ActionFilter) to achieve this on the server side. But, looking at these options I would lean towards AuthroziationFilter. The reason are:
AuthroziationFilters gets executed after AuthenticationFilters. So, it is early in the pipe line that you can make a decision of whether to get a new token or not based on expiry time. Also, we can be sure that the user is authenticated.
The scenario we are dealing with is about access_token which is related to authorization than the authentication.
With filters we have the advantage of selectively using it with actions that are explicitly decorated with that filter unlike the custom middleware which gets executed with every request. This is useful as there will be cases where you do not want to get a refreshed token (since the current one is still valid as we are getting new token well before the expiration) when you are not calling any service.
Actionfilters are called little late in the pipeline also we do not have a case for after executing method in an action filter.
Here is a question from Stackoverflow that has some nice details on how to implement an AuthorizationFilter with dependency injection.
Coming to attaching the Authorization header to the service:
This happens inside your action method. By this time you are sure that the token is valid. So I would create an abstract base class that instantiates a HttpClient class and sets the authorization header. The service class implements that base class and uses the HttpClient to call the web service. This approach is clean as consumers of your setup do not have to know how and when you are getting and attaching the token to the outgoing request for web service. Also, you are getting and attaching the refreshed access_token only when you are calling the web service.
Here is some sample code (please note that I haven't fully tested this code, this is to give you an idea of how to implement):
public class MyAuthorizeAttribute : FilterAttribute, IAuthorizationFilter
{
private const string AuthTokenKey = "Authorization";
public void OnAuthorization(AuthorizationContext filterContext)
{
var accessToken = string.Empty;
var bearerToken = filterContext.HttpContext.Request.Headers[AuthTokenKey];
if (!string.IsNullOrWhiteSpace(bearerToken) && bearerToken.Trim().Length > 7)
{
accessToken = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
}
if (string.IsNullOrWhiteSpace(accessToken))
{
// Handle unauthorized result Unauthorized!
filterContext.Result = new HttpUnauthorizedResult();
}
// call sts, get new token based on the expiration time. The grace time before which you want to
//get new token can be based on your requirement. assign it to accessToken
//Remove the existing token and re-add it
filterContext.HttpContext.Request.Headers.Remove(AuthTokenKey);
filterContext.HttpContext.Request.Headers[AuthTokenKey] = $"Bearer {accessToken}";
}
}
public abstract class ServiceBase
{
protected readonly HttpClient Client;
protected ServiceBase()
{
var accessToken = HttpContext.Current.Request.Headers["Authorization"];
Client = new HttpClient();
Client.DefaultRequestHeaders.Add("Authorization", accessToken);
}
}
public class Service : ServiceBase
{
public async Task<string> TestGet()
{
return await Client.GetStringAsync("www.google.com");
}
}
public class TestController : Controller
{
[Authorize]
public async Task<ActionResult> CallService()
{
var service = new Service();
var testData = await service.TestGet();
return Content(testData);
}
}
Please note that using the Client Credentials flow from OAuth 2.0 spec is the approach we need to take when calling an API. Also, the JavaScript solution feels more elegant for me. But, I am sure you have requirements that might be forcing you to do it the way you want. Please let me know if you have any questions are comments. Thank you.
Adding access token, refresh token and expires at to the claims and passing it to the following service may not be a good solution. Claims are more suited for identifying the user information/ authorization information. Also, the OpenId spec specifies that the access token should be sent as part of the authorization header only. We should deal with the problem of expired/ expiring tokens in a different way.
At the client, you can automate the process of getting a new access token well before its expiration using this great Javascript library oidc-client. Now you send this new and valid access token as part of your headers to the server and the server will pass it to the following APIs. As a precaution, you can use the same library to validate the expiration time of the token before sending it to the server. This is much cleaner and better solution in my opinion. There are options to silently update the token without the user noticing it. The library uses a an iframe under the hood to update the token. Here is a link for a video in which the author of the library Brock Allen explains the same concepts. The implementation of this functionality is very straightforward. Examples of how the library can be used is here. The JS call we are interested in would look like:
var settings = {
authority: 'http://localhost:5000/oidc',
client_id: 'js.tokenmanager',
redirect_uri: 'http://localhost:5000/user-manager-sample.html',
post_logout_redirect_uri: 'http://localhost:5000/user-manager-sample.html',
response_type: 'id_token token',
scope: 'openid email roles',
popup_redirect_uri:'http://localhost:5000/user-manager-sample-popup.html',
silent_redirect_uri:'http://localhost:5000/user-manager-sample-silent.html',
automaticSilentRenew:true,
filterProtocolClaims: true,
loadUserInfo: true
};
var mgr = new Oidc.UserManager(settings);
function iframeSignin() {
mgr.signinSilent({data:'some data'}).then(function(user) {
log("signed in", user);
}).catch(function(err) {
log(err);
});
}
The mgr is an instance of
FYI, we can achieve similar functionality at the server by building a custom middleware and using it as part of the request flow in a MessageHandler. Please let me know if you have any questions.
Thanks,
Soma.

Passing Authentication Token with Breeze query

My server side breeze api calls require me to validate the token value provided with each call before returning any data. To achieve this, i am passing TokenId with each Entity Query using withParameters function of breeze Entity Query and specify the parameter on my server side controller action as illustrated below.
Following is how i am doing it right now:
Client Side
function GetCustomers(){
return breeze.EntityQuery.from('Customers')
.withParameters({ TokenId: 'token value' })
.using(entityManager).execute()
}
Server Side
[HttpGet]
public IQueryable<Customer> Customers(string TokenId)
{
//server side logic
}
This for some reason looks to me can be simplified using some configuration on Breeze Entity Manager which automatically adds the parameter value to every query sent from client. And also, on server side, i don't think its necessary to have tokenId parameter defined on each controller method. This should be easy to solve with Action Filters on the server side.
Can someone point me in right direction if it is possible and how to do this?
Thanks
You should always pass custom authentication information, such as tokens, in the headers.
If you can pass it as a custom header you can search for 'define custom headers breeze.js' to see how that can be done.
A good example -
http://breeze.github.io/doc-js/server-ajaxadapter.html
Also keep in mind that if you put the token in the query string that is persisted by most of the servers that your call is crossing over, making it much easier to find tokens for your application if an evil-doer were to download the logs from that server.
Also keep in mind that if you are using cross-domain requests (CORS) you will need to enable the custom header on the server side that is receiving the call.
To give an example of the answer above:
// get the current default Breeze AJAX adapter
var ajaxAdapter: any = breeze.config.getAdapterInstance('ajax');
// set fixed headers
ajaxAdapter.defaultSettings = {
headers: {
"Bearer": this.bearerToken
}
};
Note also that if you are using ASP.net Identity (which includes a cookie based authentication), you must include this code so that the asp.net pipeline does NOT use that cookie:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new Ask.Filters.RequireHttpsAttribute());
//This forces http header authentication which is required for web api calls.
config.SuppressDefaultHostAuthentication();
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
Note also that you should use only https, with this filter:
public class RequireHttpsAttribute : AuthorizationFilterAttribute
{
public int Port { get; set; }
public RequireHttpsAttribute()
{
Port = 443;
}
public override void OnAuthorization(HttpActionContext actionContext)
{
var request = actionContext.Request;
if (request.RequestUri.Scheme != Uri.UriSchemeHttps)
{
var response = new HttpResponseMessage();
if (request.Method == HttpMethod.Get || request.Method == HttpMethod.Head)
{
var uri = new UriBuilder(request.RequestUri);
uri.Scheme = Uri.UriSchemeHttps;
uri.Port = this.Port;
response.StatusCode = HttpStatusCode.Found;
response.Headers.Location = uri.Uri;
}
else
{
response.StatusCode = HttpStatusCode.Forbidden;
}
actionContext.Response = response;
}
else
{
base.OnAuthorization(actionContext);
}
}
}
And finally, make sure you use the [Authorize] and [HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)] attribute on your controllers.
See the Full Sample and article on asp.net
I suggest using oAuth tokens in the header. I am currently using Auth0, but there are many others including Google Firebase. BreezeJS has been expanded such that it will accept AuthHttp as it's HTTP client for all calls. AuthHttp will automatically add a bearer token to the header of every call to the server.
One the server side it's a matter of adding the proper oAuth client lib (via Nuget) which automatically parses out the bearer token, validates it against the oAuth server, and makes claims available for use within your API call.
For example, in my implementation my api code will receive a claim that contains the users id, which I can then validate against my db and use to filter all actions. This keeps user B from loading user A's data by directly calling the api in a browser.
For example, all API calls are first validated by the oAuth subsystem. Once my api code is executed I know the caller has been validated and I use the passed claims (user id) to access only the calling users data - REGARDLESS what was actually requested by the API call parameters (which can be forged as easily as editing a URL string).
A simple example of this would be exposing your UserSet in the API. If you don't filter by the user claim id you must realize that every user in your system will be accessible by constructing a simple URL in a browser.
Here I filter out and return only the user record identified by the user id in the auth claim. Note I use #if AUTH simply for testing as PROD will always have AUTH turned on.
#if AUTH
[Authorize]
#endif
[HttpGet]
[EnableBreezeQuery(MaxExpansionDepth = 5)]
public IQueryable<User> UserSet()
{
#if AUTH
Guid guid = userGuid();
return _efContext.Context.UserSet.Where(x => x.active && x.guid == guid);
#else
return _efContext.Context.UserSet;
#endif
}
All of this sounds complicated, but it really is quite easy to implement.
Thanks,
Mike

Resources