AuthorizationCodeProvider: Create is never called, how do I generate the authorization code? - oauth-2.0

I'm setting up my own OAuth2 server. So far, I have succesfully implemented GrantResourceOwnerCredentials in my implementation of OAuthAuthorizationServerProvider. Now, because I am developing an app for our business, I want to implement the OAuth2 Authorization Code grant.
I have tried to follow directions here https://learn.microsoft.com/en-us/aspnet/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server but in my implementation, I have not found how to reach the Create call of the AuthorizationCodeProvider (which I set in OAuthAuthorizationServerOptions).
I have briefly checked whether accessing the TokenEndpointPath with a (wrong) code parameter works, and in the debugger I see that my AuthorizationCodeProvider's Receive call is hit. Of course there is no success because the code I send is 'sometestcode' instead of a real one, but the code is hit so that means I'm on the right path.
Here's what I have so far:
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
if (OAuthRepository.GetClient(context.ClientId) != null)
{
var expectedRootUri = new Uri(context.Request.Uri, "/");
if (context.RedirectUri.StartsWith(expectedRootUri.AbsoluteUri))
{
context.Validated();
return Task.FromResult<object>(null);
}
}
context.Rejected();
return Task.FromResult<object>(null);
}
public override Task AuthorizeEndpoint(OAuthAuthorizeEndpointContext context)
{
// I know this is wrong but it's just a start and not the focus of this SO question.
context.Response.Redirect(context.AuthorizeRequest.RedirectUri);
context.RequestCompleted();
return Task.FromResult<object>(null);
}
public override Task GrantAuthorizationCode(OAuthGrantAuthorizationCodeContext context)
{
// Needs additional checks, not the focus of my question either
var newTicket = new AuthenticationTicket(context.Ticket.Identity, context.Ticket.Properties);
context.Validated(newTicket);
return Task.FromResult<object>(null);
}
Now, when I call my AuthorizeEndpointPath with a redirect_uri, I am sent to that Uri immediately. I know this is wrong: I should be sent to a separate login page. I'll fix my Web API later to redirect to the correct Uri.
The focus of my question is this: I am now in the process of implementing the login page, but I do not know how to get the authorization code from my WebAPI after the user has logged in. (I'm skipping the consent part for now and assume that if the user is logged in they're okay with it, I'll add giving consent later.)
I am basing my flow on the diagram shared here https://docs.apigee.com/api-platform/security/oauth/oauth-v2-policy-authorization-code-grant-type
I am using Thinktecture IdentityModel to create the login page in an MVC Controller. Now I need to retrieve the authorization code from the Web API in my MVC Controller. And after that I can then redirect the user back to the original client (app) that requested the Authorization Code flow.
To obtain the authorization code from my Web API, I see three methods in Thinktecture's OAuth2Client:
CreateAuthorizeUrl
CreateCodeFlowUrl
RequestAuthorizationCodeAsync
Neither seem to do what I want. How do I proceed so that my WebAPI is called to generate the code?
[HttpGet]
[ImportModelStateFromTempData]
public ActionResult Authorize(string clientId, string returnUrl, string responseType)
{
AuthorizeViewModel viewModel = new AuthorizeViewModel();
...
...
...
return View(viewModel);
}
[HttpPost]
[ExportModelStateToTempData]
public async Task<ActionResult> Authorize(AuthorizeViewModel viewModel)
{
// NOTE: This is in MVC and is postback from *.cshtml View.
OAuth2Client.?????? // <=== How to obtain authorization code from WebAPI?
...
return Redirect(returnUrl);
}
I think I have it correctly setup on the Web API side. I just don't know how to hit the Create part of the flow. I hope someone can help me understand what I am not seeing. I have a blind spot somewhere I think...
How do I have OAuth2Client get me the authorization code from my WebAPI?
I am also using Postman to test my Web API. If anyone can help me get the URL in Web API 2.0 that returns an authorization code, I would also accept that as an answer. Then I can write the code in MVC myself.
Edit
Okay, so I think I found a part of my blind spot. Firstly, I marked `AuthorizeEndpoint' as "not the focus of this SO question", but that was a big mistake.
When I adapt the AuthorizeEndpoint like so:
public override Task AuthorizeEndpoint(OAuthAuthorizeEndpointContext context)
{
System.Security.Claims.ClaimsIdentity ci = new System.Security.Claims.ClaimsIdentity("Bearer");
context.OwinContext.Authentication.SignIn(ci);
context.RequestCompleted();
return Task.FromResult<object>(null);
}
And if I adapt my implementation of AuthorizationCodeProvider.Create like so:
public void Create(AuthenticationTokenCreateContext context)
{
context.Ticket.Properties.IssuedUtc = DateTime.UtcNow;
context.Ticket.Properties.ExpiresUtc = DateTime.UtcNow.AddSeconds(60);
// Some random Guid
context.SetToken(Guid.NewGuid().ToString("n"));
}
Any call to /authorize is redirected to redirect_uri with a query parameter code=<THE_RANDOM_GUID>! :D
Obviously, this implementation is not where it should be, so my question is not yet resolved. Remaining issues:
Right now, anybody can request an authorization code, the client_id is ignored. ValidateClientAuthentication is apparently not hit as part of AuthorizeEndpoint. How do I obtain ClientId in AuthorizeEndpoint?
The authorization code is not coupled to a client. Anyone who intercepts the code could use it. How do I obtain the ClientId in AuthorizationCodeProvider.Create so that I can store it with the code?
The authorization code is not coupled to a user at all, it's an empty ClaimsIdentity. How do I put a user-login page in between and in AuthorizeEndpoint obtain the ClaimsIdentity for the logged-in user?

So, after quite some searching online, I got some success by searching github. Apparently, OAuthAuthorizationServerProvider offers AuthorizeEndpoint and that method should be used for both "Hey, you're not authorized, go log in you!" as well as for "Ahh, okay you're cool, here's an authorization code.". I had expected that OAuthAuthorizationServerProvider would have two separate methods for that, but it doesn't. That explains why on github, I find some projects that implement AuthorizeEndpoint in a rather peculiar way. I've adopted this. Here's an example:
public override async Task AuthorizeEndpoint(OAuthAuthorizeEndpointContext context)
{
if (context.Request.User != null && context.Request.User.Identity.IsAuthenticated)
{
var redirectUri = context.Request.Query["redirect_uri"];
var clientId = context.Request.Query["client_id"];
var authorizeCodeContext = new AuthenticationTokenCreateContext(
context.OwinContext,
context.Options.AuthorizationCodeFormat,
new AuthenticationTicket(
(ClaimsIdentity)context.Request.User.Identity,
new AuthenticationProperties(new Dictionary<string, string>
{
{"client_id", clientId},
{"redirect_uri", redirectUri}
})
{
IssuedUtc = DateTimeOffset.UtcNow,
ExpiresUtc = DateTimeOffset.UtcNow.Add(context.Options.AuthorizationCodeExpireTimeSpan)
}));
await context.Options.AuthorizationCodeProvider.CreateAsync(authorizeCodeContext);
context.Response.Redirect(redirectUri + "?code=" + Uri.EscapeDataString(authorizeCodeContext.Token));
}
else
{
context.Response.Redirect("/account/login?returnUrl=" + Uri.EscapeDataString(context.Request.Uri.ToString()));
}
context.RequestCompleted();
}
Source: https://github.com/wj60387/WebApiOAUthBase/blob/master/OwinWebApiBase/WebApiOwinBase/Providers/OAuthServerProvider.cs
As for my remaining three questions:
Right now, anybody can request an authorization code, the client_id is ignored. ValidateClientAuthentication is apparently not hit as part of AuthorizeEndpoint. How do I obtain ClientId in AuthorizeEndpoint?
Answer: You have to implement `ValidateClientAuthentication'.
The authorization code is not coupled to a client. Anyone who intercepts the code could use it. How do I obtain the ClientId in AuthorizationCodeProvider.Create so that I can store it with the code?
Answer: OAuthAuthorizationServerProvider takes care of this. As long as you set "client_id" in the ticket, it will check that the client that requests an access token for the authorization code is the same.
The authorization code is not coupled to a user at all, it's an empty ClaimsIdentity. How do I put a user-login page in between and in AuthorizeEndpoint obtain the ClaimsIdentity for the logged-in user?
Answer: You create a separate login page. What this does is sign the user in. If your WebAPI uses cookie-based authentication, you can just redirect the user to the AuthorizeEndpoint again. If you use access tokens, your login page has to make a request to `AuthorizeEndpoint' with the access token to obtain an authorization code. (Don't give the access token to the third party. Your login page requests the authorization code and sends that back.) In other words, if you use access tokens then there are two clients involved in this flow.

Related

How to implement OAuth2 for a single tool, without using it as my application's authorization solution

I currently have a MVC site, in .NET Core, backed by a public API. My users must log in (there are no [Anonymous] controllers), and authentication is already successfully being done using the DotNetCore.Authentication provider. All that is well and good.
What I'm now trying to do (by user request) is implement functionality for a user to read and view their Outlook 365 calendar on a page within my site. It doesn't seem too hard on the surface... all I have to do is have them authenticate through microsoftonline with my registered app, and then -- once they have given approval -- redirect back to my app to view their calendar events that I am now able to pull (probably using Graph).
In principle that seems really easy and straightforward. My confusion comes from not being able to implement authentication for a single controller, and not for the entire site. All of the OAuth2 (or OpenID, or OWIN, or whatever your flavor) examples I can find online -- of which there are countless dozens -- all want to use the authorization to control the User.Identity for the whole site. I don't want to change my sitewide authentication protocol; I don't want to add anything to Startup.cs; I don't want anything to scope outside of the one single controller.
tldr; Is there a way to just call https://login.microsoftonline.com/common/oauth2/v2.0/authorize (or facebook, or google, or whatever), and get back a code or token that I can use for that user on that area of the site, and not have it take over the authentication that is already in place for the rest of the site?
For anybody else who is looking for this answer, I've figured out (after much trial and error) how to authenticate for a single user just for a short time, without using middleware that authenticates for the entire application.
public async Task<IActionResult> OfficeRedirectMethod()
{
Uri loginRedirectUri = new Uri(Url.Action(nameof(OfficeAuthorize), "MyApp", null, Request.Scheme));
var azureADAuthority = #"https://login.microsoftonline.com/common";
// Generate the parameterized URL for Azure login.
var authContext = GetProviderContext();
Uri authUri = await authContext.GetAuthorizationRequestUrlAsync(_scopes, loginRedirectUri.ToString(), null, null, null, azureADAuthority);
// Redirect the browser to the login page, then come back to the Authorize method below.
return Redirect(authUri.ToString());
}
public async Task<IActionResult> OfficeAuthorize()
{
var code = Request.Query["code"].ToString();
try
{
// Trade the code for a token.
var authContext = GetProviderContext();
var authResult = await authContext.AcquireTokenByAuthorizationCodeAsync(code, _scopes);
// do whatever with the authResult, here
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine(ex.ToString());
}
return View();
}
public ConfidentialClientApplication GetContext()
{
var clientId = "OfficeClientId;
var clientSecret = "OfficeClientSecret";
var loginRedirectUri = new Uri(#"MyRedirectUri");
TokenCache tokenCache = new MSALSessionCache().GetMsalCacheInstance();
return new ConfidentialClientApplication(
clientId,
loginRedirectUri.ToString(),
new ClientCredential(clientSecret),
tokenCache,
null);
}
I don't know if that will ever be helpful to anybody but me; I just know that it's a problem that doesn't seem to be easily solved by a quick search.

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.

How to add parameters to redirect_uri in WebApi Oauth Owin authentication process?

I'm creating a webapi project with oauth bearer token authenthication and external login providers (google, twitter, facebook etc.). I started with the basic VS 2013 template and got everything to work fine!
However, after a user successfully logs is, the owin infrastructure creates a redirect with the folllowing structure:
http://some.url/#access_token=<the access token>&token_type=bearer&expires_in=1209600
In my server code I want to add an additional parameter to this redirect because in the registration process of my app, a new user needs to first confirm and accept the usage license before he/she is registered as a user. Therefore I want to add the parameter "requiresConfirmation=true" to the redirect. However, I've no clue about how to do this. I tried setting AuthenticationResponseChallenge.Properties.RedirectUri of the AuthenticationManager but this doesn't seem to have any affect.
Any suggestions would be greatly appreciated!
It should be relatively easy with the AuthorizationEndpointResponse notification:
In your custom OAuthAuthorizationServerProvider implementation, simply override AuthorizationEndpointResponse to extract your extra parameter from the ambient response grant, which is created when you call IOwinContext.Authentication.SignIn(properties, identity).
You can then add a custom requiresConfirmation parameter to AdditionalResponseParameters: it will be automatically added to the callback URL (i.e in the fragment when using the implicit flow):
public override Task AuthorizationEndpointResponse(OAuthAuthorizationEndpointResponseContext context) {
var requiresConfirmation = bool.Parse(context.OwinContext.Authentication.AuthenticationResponseGrant.Properties.Dictionary["requiresConfirmation"]);
if (requiresConfirmation) {
context.AdditionalResponseParameters.Add("requiresConfirmation", true);
}
return Task.FromResult<object>(null);
}
In your code calling SignIn, determine whether the user is registered or not and add requiresConfirmation to the AuthenticationProperties container:
var properties = new AuthenticationProperties();
properties.Dictionary.Add("requiresConfirmation", "true"/"false");
context.Authentication.SignIn(properties, identity);
Feel free to ping me if you need more details.

Using OpenAM OAuth2 with MVC4 and OAuthWebSecurity

Before trying MVC5, I had a go at using OpenAM with MVC4, with slightly better results. I need to authenticate from an asp.net application using OpenAM, and don't fancy the Fedlet route - I can see anybody else who has ever tried that
So, this was my starting point. This shows how to use Google and Facebook, and it works a treat. This goes on to show how to use other providers. I'm using OpenAM with OAuth2, so created "OpenAMClient", inheriting from DotNetOpenAuth.AspNet.Clients.OAuth2Client, and registered this in AuthConfig.RegisterAuth:
OAuthWebSecurity.RegisterClient(new OpenAMClient("consumerKey", "consumerSecret"));
This is great, because it now appears automatically on the login page, and pretty much worked perfectly until I started to use it for authentication. In the GetServiceLoginUrl override, I constructed a path to my OpenAM server, appending the returnURL that had been generated by the application and passed in as a parameter:
protected override Uri GetServiceLoginUrl(Uri returnUrl)
{
var response =
string.Format(
"http://localopenam.hibu.com:9080/openam_10.1.0/oauth2/authorize?client_id={0}&response_type=code&scope=email&redirect_uri={1}",
_consumerKey, returnUrl);
return new Uri(response);
}
This got me to my OpenAM server login page, but after authenticating, I got an error saying that the redirection URI wasn't acceptable. Debugging the code, I can see that the ReturnURL starts off in the ExternalLoginResult as "/Account/ExternalLoginCallback", but by the time it reaches GetServiceLoginUrl, it has become:
http://localhost:60448/Account/ExternalLoginCallback?__provider__=OpenAM&__sid__=12e299cbac474b60a935f946f69d04a8
OpenAM isn't having any of that, as the "sid" parameter is dynamic, and it doesn't seem to acccept wildcards - it won't allow the returnURL provided by OAuthWebSecurity.
As a workaround, I intercept the ReturnURL, and switch to a new AccountController method:
protected override Uri GetServiceLoginUrl(Uri returnUrl)
{
var workingUrl = "http://localhost:60448/Account/OpenAMCallback";
var response =
string.Format(
"http://localopenam.hibu.com:9080/openam_10.1.0/oauth2/authorize?client_id={0}&response_type=code&scope=email&redirect_uri={1}",
_consumerKey, workingUrl);
return new Uri(response);
}
I add http://localhost:60448/Account/OpenAMCallback as a redirectURL in OpenAM, then added AccountController OpenAMCallback:
public ActionResult OpenAMCallback(string code)
{
Console.WriteLine(code );
//use the code to get the token, then user details etc
return RedirectToLocal(null);
}
This is great, because from here I get the access code, so I can make more requests for the token, get all the allowed user details, all of that kind of thing, but ... I'm jealous of the original ExternalLoginCallback method I've subverted away from, that all the other cool authentication servers use. I want to use OAuthWebSecurity.VerifyAuthentication and OAuthWebSecurity.GetOAuthClientData, but VerifyAuthentication is coming back as null, so that stops that party
I can use http://dotnetopenauth.net/ and do it by hand, but I'd rather use a framework so there's less to maintain. Am I missing something obvious, or is OAuthWebSecurity not really up to this so I should stick with dotnetopenauth?
thanks

Cross platform authentication using ASP.NET Web API

How do I even begin coding authentication using ASP.NET Web API so it is cross-platform to support desktop, mobile and web? I'd read of some methods of doing RESTful authentication, such as using tokens in the header.
Are there any example projects out there that utilizes this method?
Questions:
If not how do I fix the [Authorize] attribute to read the token?
How do I generate this token? I dont think i can use formsauthentication because that uses cookies.
How do I handle the actual authorization, do the client send raw password and username then I generate the token or is there some other way?
How do I handle when my website is using it? I heard this is handled differently than when an app is using it, such as getting the domain and authorizing it.
I think tokens would be a solid way to go. Forms authentication is based on cookies for the web. Not the most idea situation for all non browser clients though.
What I'd suggest is creating a custom AuthorizationFilterAttribute and overriding the OnAuthorization method. In that method, you could check for the existence of a token that you've issued to the client after they've supplied valid credentials. You can use this attribute on any method or controller you want validated. Here's a sample you might reference
public class AuthorizeTokenAttribute : AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
if (actionContext != null)
{
if (!AuthorizeRequest(actionContext.ControllerContext.Request))
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized) { RequestMessage = actionContext.ControllerContext.Request };
}
return;
}
}
private bool AuthorizeRequest(System.Net.Http.HttpRequestMessage request)
{
bool authorized = false;
if (request.Headers.Contains(Constants.TOKEN_HEADER))
{
var tokenValue = request.Headers.GetValues("TOKEN_HEADER");
if (tokenValue.Count() == 1) {
var value = tokenValue.FirstOrDefault();
//Token validation logic here
//set authorized variable accordingly
}
}
return authorized;
} }
TOKEN_HEADER is just a string representing an HTTP header that the client should pass back for authenticated requests.
So let's walk through it
Client requests secure data
Client is not authorized, return a response with an Unauthorized status code
Client sends credentials to authenticate, which should be secured via HTTPS
Once validated, client receives a token via an HTTP header, or whatever works for you
Client tries requesting secure data again, this time attached the token to the request
The AuthorizeTokenAttribute will validate the token and allow the action to execute.
Also, check this post by John Petersen. Making your ASP.NET Web API’s secure
There are lots of ways to authenticate users for a REST service. Using tokens is possible but just using Basic Authentication is even simpler and about as standard and cross platform as you can go.
Don't confuse authorization with authentication. The [Authorize] attribute is all about authorization but only after a user has been authenticated using some other mechanism. Authorization is completely useless without doing proper authentication first.
The best resource to check is Dominick Baier who is an expert on the subject.
I use a very simple approach:
define an access profile with its unique accessId and accessKey (e.g. MD5 hashed GUID value)
store such access profile in database
every request (GET/POST/etc.) must supply accessId, queryHash (MD5 hash value represents the query) and signature (MD5 hash value of queryHash + accessKey). Of course the client needs keep the accessKey in a secure place!!!
server gets the request will check the accessId and the signature using the same calculation algorithm to reject or grant the access (authenticate)
further authorization can be done on request type basis utilizing the access profile
the service with this approach using the new ASP.NET MVC web API can serve whatever type of client: browser/javascript and native(desktop or mobile) etc.
U can use ActionFilterAttribute and override the OnActionExecuting method.
Later on register this filter in global.cs to apply this filter for all the actions like this in Application Start method
var config = GlobalConfiguration.Configuration;
config.Filters.Add(new CustomAuthAttribute ());
{
namespace Customss
{
Public class CustomAuthAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
// To inforce HTTPS if desired , else comment out the code
if (!String.Equals(actionContext.Request.RequestUri.Scheme, "https", StringComparison.OrdinalIgnoreCase))
{
actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest)
{
Content = new StringContent("HTTPS Required")
};
return;
}
// get toekn from the header
var userToken = actionContext.Request.Headers.GetValues("UserToken");
// Customer Logic to check the validity of the token.
// U can have some DB logic to check , custom STS behind or some loca cache used to compare the values
}
}
}
}

Resources