I am trying to do an authenticated web api request that does not reset the authentication cookie timeout. In the MVC world I would accomplish this by removing the FormsAuthenication cookie from the responce:
Response.Cookies.Remove(System.Web.Security.FormsAuthentication.FormsCookieName);
In Web API 2 I wrote a custom IHttpActionResult, and I am removing the Set-Cookie header from the response. This is however, not removing the header, as I still see the Set-Cookie header when the auth cookie is being updated for the requests that use this action result.
Here is the custom IHttpActionResult:
public class NonAuthResetResult<T> : IHttpActionResult where T: class
{
private HttpRequestMessage _request;
private T _body;
public NonAuthResetResult(HttpRequestMessage request, T body)
{
_request = request;
_body = body;
}
public string Message { get; private set; }
public HttpRequestMessage Request { get; private set; }
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var msg = _request.CreateResponse(_body);
msg.Headers.Remove("Set-Cookie");
return Task.FromResult(msg);
}
}
How do I edit the response header in Web API 2, because this is not working.
Global.asax can remove cookies in the Application_EndRequest event. And you can set a variable to be later picked up by Application_EndRequest.
Step 1. Create an action filter which sets a variable in Context.Items:
public class NoResponseCookieAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
System.Web.HttpContext.Current.Items.Add("remove-auth-cookie", "true");
}
}
Step 2. Handle the Application_EndRequest event in your global.asax file. If the variable from Step 1 is present, remove the cookie.
protected void Application_EndRequest()
{
if (HttpContext.Current.Items["remove-auth-cookie"] != null)
{
Context.Response.Cookies.Remove(System.Web.Security.FormsAuthentication.FormsCookieName);
}
}
Step 3. Decorate your web api actions with the custom filter:
[NoResponseCookie]
public IHttpActionResult GetTypes()
{
// your code here
}
If you're using Web API 2, you're probably using the OWIN Cookie Middleware. What you are describing sounds like you want to disable the sliding expiry window on the auth cookie.
In the standard Web API template, you should have an App_Start/Startup.Auth.cs. In it you'll find the line...
app.UseCookieAuthentication(new CookieAuthenticationOptions());
This enables and configures the cookie middleware. You can pass in some options to change the timeout window and disable sliding expiry...
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
SlidingExpiration = false,
ExpireTimeSpan = new TimeSpan(1, 0, 0) // 1 hour
});
Related
I followed this tutorial: https://medium.com/#st.mas29/microsoft-blazor-web-api-with-jwt-authentication-part-1-f33a44abab9d
I downloaded the example: https://github.com/StuwiiDev/DotnetCoreJwtAuthentication/tree/Part2
I can see that the token is created but I don't understand how it is or should be saved on the client side as each time I access the SampleDataController, which has the Authorize tag, it returns a 401.
When calling and adding the token using Postman it works.
What am I missing for my user to be authenticated? Doesn't Microsoft.AspNetCore.Authentication.JwtBearer handle the client part (storing the token)?
What am I missing for my user to be authenticated? Doesn't Microsoft.AspNetCore.Authentication.JwtBearer handle the client part (storing the token)?
The JwtBearer runs on server side , it will only validate the authorization header of request, namely Authorization: Bearer your_access_token, and won't care about how you WebAssembly codes runs . So you need send the request with a jwt accessToken . Since the tutorial suggests you should use localStorage , let's store the accessToken with localStorage .
Because WebAssembly has no access to BOM yet, we need some javascript codes served as glue . To do that, add a helper.js under the JwtAuthentication.Client/wwwroot/js/ :
var wasmHelper = {};
wasmHelper.ACCESS_TOKEN_KEY ="__access_token__";
wasmHelper.saveAccessToken = function (tokenStr) {
localStorage.setItem(wasmHelper.ACCESS_TOKEN_KEY,tokenStr);
};
wasmHelper.getAccessToken = function () {
return localStorage.getItem(wasmHelper.ACCESS_TOKEN_KEY);
};
And reference the script in your JwtAuthentication.Client/wwwroot/index.html
<body>
<app>Loading...</app>
<script src="js/helper.js"></script>
<script src="_framework/blazor.webassembly.js"></script>
</body>
Now, let's wrap the javascript codes into C# . Create a new file Client/Services/TokenService.cs:
public class TokenService
{
public Task SaveAccessToken(string accessToken) {
return JSRuntime.Current.InvokeAsync<object>("wasmHelper.saveAccessToken",accessToken);
}
public Task<string> GetAccessToken() {
return JSRuntime.Current.InvokeAsync<string>("wasmHelper.getAccessToken");
}
}
Register this service by :
// file: Startup.cs
services.AddSingleton<TokenService>(myTokenService);
And now we can inject the TokenService into Login.cshtml and use it to save token :
#using JwtAuthentication.Client.Services
// ...
#page "/login"
// ...
#inject TokenService tokenService
// ...
#functions {
public string Email { get; set; } = "";
public string Password { get; set; } = "";
public string Token { get; set; } = "";
/// <summary>
/// response from server
/// </summary>
private class TokenResponse{
public string Token;
}
private async Task SubmitForm()
{
var vm = new TokenViewModel
{
Email = Email,
Password = Password
};
var response = await Http.PostJsonAsync<TokenResponse>("http://localhost:57778/api/Token", vm);
await tokenService.SaveAccessToken(response.Token);
}
}
Let's say you want to send data within FetchData.cshtml
#functions {
WeatherForecast[] forecasts;
protected override async Task OnInitAsync()
{
var token = await tokenService.GetAccessToken();
Http.DefaultRequestHeaders.Add("Authorization",String.Format("Bearer {0} ",token));
forecasts = await Http.GetJsonAsync<WeatherForecast[]>("api/SampleData/WeatherForecasts");
}
}
and the result will be :
Apologies in advance as this is somewhat responding to a previous answer, but I don't have the rep to comment on that.
If it helps anyone else who was similarly looking for a solution to using JWT in a Blazor app, I found #itminus answer incredibly useful, but it also pointed me to another course.
One problem I found was that calling FetchData.cshtml a second time would blow up when it tries to add the Authorization header a second time.
Instead of adding the default header there, I added it to the HttpClient singleton after a successful login (which I believe Blazor creates for you automatically). So changing SubmitForm in Login.cshtml from #itminus' answer.
protected async Task SubmitForm()
{
// Remove any existing Authorization headers
Http.DefaultRequestHeaders.Remove("Authorization");
TokenViewModel vm = new TokenViewModel()
{
Email = Email,
Password = Password
};
TokenResponse response = await Http.PostJsonAsync<TokenResponse>("api/Token/Login", vm);
// Now add the token to the Http singleton
Http.DefaultRequestHeaders.Add("Authorization", string.Format("Bearer {0} ", response.Token));
}
Then I realised, than as I'm building a SPA, so I didn't need to persist the token across requests at all - it's just in attached to the HttpClient.
The following class handle the login process on the client, storing the JWT token in local storage. Note: It is the developer responsibility to store the JWT token, and passes it to the server. The client (Blazor, Angular, etc.) does not do that for him automatically.
public class SignInManager
{
// Receive 'http' instance from DI
private readonly HttpClient http;
public SignInManager(HttpClient http)
{
this.http = http;
}
[Inject]
protected LocalStorage localStorage;
public bool IsAuthenticated()
{
var token = localStorage.GetItem<string>("token");
return (token != null);
}
public string getToken()
{
return localStorage.GetItem<string>("token");
}
public void Clear()
{
localStorage.Clear();
}
// model.Email, model.Password, model.RememberMe, lockoutOnFailure: false
public async Task<bool> PasswordSignInAsync(LoginViewModel model)
{
SearchInProgress = true;
NotifyStateChanged();
var result = await http.PostJsonAsync<Object>("/api/Account", model);
if (result)// result.Succeeded
{
_logger.LogInformation("User logged in.");
// Save the JWT token in the LocalStorage
// https://github.com/BlazorExtensions/Storage
await localStorage.SetItem<Object>("token", result);
// Returns true to indicate the user has been logged in and the JWT token
// is saved on the user browser
return true;
}
}
}
// This is how you call your Web API, sending it the JWT token for // the current user
public async Task<IList<Profile>> GetProfiles()
{
SearchInProgress = true;
NotifyStateChanged();
var token = signInManager.getToken();
if (token == null) {
throw new ArgumentNullException(nameof(AppState)); //"No token";
}
this.http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
// .set('Content-Type', 'application/json')
// this.http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
Profiles = await this.http.GetJsonAsync<Profile[]>("/api/Profiles");
SearchInProgress = false;
NotifyStateChanged();
}
// You also have to set the Startup class on the client as follows:
public void ConfigureServices(IServiceCollection services)
{
// Add Blazor.Extensions.Storage
// Both SessionStorage and LocalStorage are registered
// https://github.com/BlazorExtensions/Storage
**services.AddStorage();**
...
}
// Generally speaking this is what you've got to do on the client. // On the server, you've got to have a method, say in the Account controller, whose function is to generate the JWT token, you've to configure the JWT middleware, to annotate your controllers with the necessary attribute, as for instance:
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
and so on...
Hope this helps...
Purpose:
I work with a third party that has implemented an OAuth2 authorization code flow. The goal is for an authenticated user in my app to be able to access areas of a this third parties app without having to sign in to the third party. I have provided the third party with our authorization url, a clientId and a callback url (not sure how this is used).
I am trying to wire up a basic authorization code flow using OAuth2, but I am stuck on an invalid_grant error and am having trouble determining how the process is intended to work.
Here is what I have implemented thus far:
In the startup class I configure my OAuth server to include an AuthorizeEndpointPath and an AuthorizationCodeProvider:
var allowInsecureHttp = bool.Parse(ConfigurationManager.AppSettings["AllowInsecureHttp"]);
var oAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = allowInsecureHttp,
TokenEndpointPath = new PathString("/oauth2/token"),
AuthorizeEndpointPath = new PathString("/oauth2/authorize"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
Provider = new CustomOAuthProvider(HlGlobals.Kernel),
AccessTokenFormat = new CustomJwtFormat(_baseUrl, HlGlobals.Kernel),
AuthorizationCodeProvider = new SimpleAuthenticationTokenProvider()
};
The CustomOAuthProider contains the following overrides:
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
//We validated that Client Id and redirect Uri are what we expect
if (context.ClientId == "123456" && context.RedirectUri.Contains("localhost"))
{
context.Validated();
}
else
{
context.Rejected();
}
return Task.FromResult<object>(null);
}
public override Task AuthorizeEndpoint(OAuthAuthorizeEndpointContext context)
{
var ci = new ClaimsIdentity("Bearer");
context.OwinContext.Authentication.SignIn(ci);
context.RequestCompleted();
return Task.FromResult<object>(null);
}
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string clientId;
string clientSecret;
Guid clientIdGuid;
// Validate the context
context.Validated();
return Task.FromResult<object>(0);
}
Finally, the code in the SimpleAuthenticationTokenProvider is implemented as follows:
public SimpleAuthenticationTokenProvider() : base()
{
this.OnCreate = CreateCode;
this.OnReceive = ReceiveCode;
}
private readonly ConcurrentDictionary<string, string> _authenticationCodes = new ConcurrentDictionary<string, string>(StringComparer.Ordinal);
public void CreateCode(AuthenticationTokenCreateContext context)
{
context.SetToken(Guid.NewGuid().ToString("n") + Guid.NewGuid().ToString("n"));
_authenticationCodes[context.Token] = context.SerializeTicket();
}
public void ReceiveCode(AuthenticationTokenReceiveContext context)
{
string value;
_authenticationCodes.TryGetValue(context.Token, out value);
context.DeserializeTicket(value);
}
I have wired up a sample request inside of Postman. When I execute the postman oauth2 Authroization Code grant type the following methods from above fire sequentially:
ValidateClientRedirectUri
AuthorizeEndpoint
ValidateClientAuthentication
ReceiveCode
I am getting back an 'invalid_grant' error from postman, and no authorization code has been returned.
Can anyone point out where I may be going wrong here?
Secondly, how is the callbackurl/redirect url supposed to be used? Is it a fallback to the login page if the user has not authenticated?
UPDATE
I see that a 'code' is coming back in the querystring attached to the redirectURL on the response. Should the redirectURL be a page on the third-party site?
How is this code exchanged for a token?
UPDATE 2
Another question: At what phase do I read/parse the code? I can see the code coming in on the request querystring in my GrantClientCredentials method. Should I just parse it from the querystring and validate? At what point should ReceiveCode be called?
I have read that it's possible to create a custom Owin authentication handler, but I can't figure out how to configure Owin to use my handler instead of the default one.
How do I tell Owin to use this class instead of the default?
public class XDOpenIdAuthHandler: OpenIdConnectAuthenticationHandler
{
public XDOpenIdAuthHandler(ILogger logger)
: base(logger)
{
}
protected override void RememberNonce(OpenIdConnectMessage message, string nonce)
{
//Clean up after itself, otherwise cookies keep building up until we've got over 100 and
// the browser starts throwing errors. Bad OpenId provider.
var oldNonces = Request.Cookies.Where(kvp => kvp.Key.StartsWith(OpenIdConnectAuthenticationDefaults.CookiePrefix + "nonce")).ToArray();
if (oldNonces.Any())
{
CookieOptions cookieOptions = new CookieOptions
{
HttpOnly = true,
Secure = Request.IsSecure
};
foreach (KeyValuePair<string, string> oldNonce in oldNonces)
{
Response.Cookies.Delete(oldNonce.Key, cookieOptions);
}
}
base.RememberNonce(message, nonce);
}
}
You must add it as a part of a custom AuthenticationMiddleware.
public class CustomAuthMiddleware : AuthenticationMiddleware<OpenIdConnectAuthenticationOptions>
{
public CustomAuthMiddleware(OwinMiddleware nextMiddleware, OpenIdConnectAuthenticationOptions authOptions)
: base(nextMiddleware, authOptions)
{ }
protected override AuthenticationHandler<OpenIdConnectAuthenticationOptions> CreateHandler()
{
return new XDOpenIdAuthHandler(yourLogger);
}
}
Then using it in the Startup.Auth for example:
public partial class Startup
{
// For more information on configuring authentication, please visit https://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
app.Use<CustomAuthMiddleware>(new OpenIdConnectAuthenticationOptions());
}
}
Be aware however that the Owin pipeline must not contain the default OpenIdConnectAuthenticationMiddleware, otherwise it will still get called as part of the request pipe.
I am using a webapi project as my auth Server and also resource server. The intention is to access the serivice form an Android app. I also want a web front end which is being written in an MVC app. I originally used the default MVC auth but have moved to web pai handing out tokens. I can recieve the auth token form the webapi service and I am sending the token to the client in a cookie although I may just cache is client side. I currently have the following OAuthBearerAuthenticationProvider running:
public class CookieOAuthBearerProvider : OAuthBearerAuthenticationProvider
{
public override Task RequestToken(OAuthRequestTokenContext context)
{
base.RequestToken(context);
var value = context.Request.Cookies["AuthToken"];
if (!string.IsNullOrEmpty(value))
{
context.Token = value;
}
return Task.FromResult<object>(null);
}
}
and in my startup class I have this method:
private void ConfigureAuth(IAppBuilder app)
{
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()
{
Provider = new CookieOAuthBearerProvider(),
});
}
which I call in the Configuration method.
The bit I seem to be missing is how to tap into converting my token into the logged in user. I cant seem to figure out where the deserializtion happens. I have tried changing my configueAuth to:
private void ConfigureAuth(IAppBuilder app)
{
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()
{
Provider = new CookieOAuthBearerProvider(),
AccessTokenProvider = new AuthenticationTokenProvider()
{
OnReceive = receive
}
});
}
public static Action<AuthenticationTokenReceiveContext> receive = new Action<AuthenticationTokenReceiveContext>(c =>
{
c.DeserializeTicket(c.Token);
c.OwinContext.Environment["Properties"] = c.Ticket.Properties;
});
and my receive method is being called. The AuthenticationTokenReceiveContext has my token attached but the DeserializeTicket is returning null. Can anyone advise what I am missing to get the User details form this token?
UPDATE as per suggested answer below. The Statrup code and OAuthBearerAuthenticationOptions now like like this:
public class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
private void ConfigureAuth(IAppBuilder app)
{
OAuthOpt = new OAuthBearerAuthenticationOptions()
{
Provider = new CookieOAuthBearerProvider(),
AccessTokenProvider = new AuthenticationTokenProvider()
{
OnReceive = receive
}
};
app.UseOAuthBearerAuthentication(OAuthOpt);
}
public static Action<AuthenticationTokenReceiveContext> receive = new Action<AuthenticationTokenReceiveContext>(c =>
{
var ticket = OAuthOpt.AccessTokenFormat.Unprotect(c.Token);
});
public static OAuthBearerAuthenticationOptions OAuthOpt { get; private set; }
}
but I am still getting a null value out. Could I be missing some relevant option on the OAuthBearerAuthenticationOptions?
Try this.
Save the OAuthBearerAuthenticationOptions you are instantiating inline to a static variable named OAuthOpt (or anything you like) in Startup.Auth and use the code below wherever you want to retrieve the user information.
Microsoft.Owin.Security.AuthenticationTicket ticket = Startup.OAuthOpt.AccessTokenFormat.Unprotect(token);`
I suggest you make use of Json Web Tokens (JWT) and customize the token generation using a CustomOAuthProvider. Here is a good resource from Taiseer Joudeh on how to do this. You will have to use this nuget package to decode the bearer tokens.
I was using ASP.NET MVC framework. In this framework, we checked every incoming request (url) for some key and assigned it to a property. We created a custom class which derived from Controller class & we override OnActionExecuting() to provide our custom logic.
How can we achieve the same in ASP.NET WEB API?
//Implementation from ASP.NET MVC
public class ApplicationController : Controller
{
public string UserID { get; set; }
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!string.IsNullOrEmpty(Request.Params["uid"]))
UserID = Request.Params["uid"];
base.OnActionExecuting(filterContext);
}
}
What I have tried in ASP.NET WEB API: -- Though this is working, I wonder if this is the correct approach?
Created a base controller
public class BaseApiController : ApiController
{
public string UserID { get; set; }
}
Created another class which inherits ActionFilterAttribute class & I override OnActionExecuting()
public class TokenFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
var queryString = actionContext.Request.RequestUri.Query;
var items = HttpUtility.ParseQueryString(queryString);
var userId = items["uid"];
((MyApi.Data.Controllers.BaseApiController)(actionContext.ControllerContext.Controller)).UserID = userId;
}
}
Now register this class
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new TokenFilterAttribute());
}
You can use message handlers from ASP.NET Web API. It is a typical security scenation, when you need to get some user token from query string, URL or HTTP Header
http://www.asp.net/web-api/overview/advanced/http-message-handlers
1.When you need simply to extract userId from URL, then use it as parameter for your Api method and ASP.NET WebAPI will do work for you, like
[HttpGet, Route("{userId}/roles")]
public UserRoles GetUserRoles(string userId, [FromUri] bool isExternalUser = true)
It work for such request
http://.../15222/roles?isExternalUser=false
2.If it is security scenario, please refer to http://www.asp.net/web-api/overview/security/authentication-and-authorization-in-aspnet-web-api
Basically you will need some MessageHandler or you can use filter attributes as well, it is mechanism in ASP.NET Web API to intercept each call.
If you need to process each request then MessageHandler is your way. You need implement MessageHanler and then register it.
To say easily, typical MessageHandler is class derived from MessageHandler or DelegatingHandler with SendAsync method overriden:
class AuthenticationHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// Your code here
return base.SendAsync(request, cancellationToken);
}
}
And you need register it
static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Other code for WebAPI registerations here
config.MessageHandlers.Add(new AuthenticationHandler());
}
}
and call it from Global.asax.cs
WebApiConfig.Register(GlobalConfiguration.Configuration);
Some example dummy hypotetical implementation of such handler (here you need to imeplement your UidPrincipal from IPrincipal and UidIdentity from IIdentity)
public class AuthenticationHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
try
{
var queryString = actionContext.Request.RequestUri.Query;
var items = HttpUtility.ParseQueryString(queryString);
var userId = items["uid"];
// Here check your UID and maybe some token, just dummy logic
if (userId == "D8CD2165-52C0-41E1-937F-054F24266B65")
{
IPrincipal principal = new UidPrincipal(new UidIdentity(uid), null);
// HttpContext exist only when hosting as asp.net web application in IIS or IISExpress
if (HttpContext.Current != null)
{
HttpContext.Current.User = principal;
}
else
{
Thread.CurrentPrincipal = principal;
}
return base.SendAsync(request, cancellationToken);
}
catch (Exception ex)
{
this.Log().Warn(ex.ToString());
return this.SendUnauthorizedResponse(ex.Message);
}
}
else
{
return this.SendUnauthorizedResponse();
}
}
catch (SecurityTokenValidationException)
{
return this.SendUnauthorizedResponse();
}
}
}
And lets access it from some ASP.NET WebApi method or some property in WebAPI class
var uid = ((UidIdentity)User.Identity).Uid