//TODO: Need to follow more of this for http://bitoftech.net/2014/10/27/json-web-token-asp-net-web-api-2-jwt-owin-authorization-server/ dealing with AudienceStores //another interesting one https://blog.jayway.com/2014/09/25/securing-asp-net-web-api-endpoints-using-owin-oauth-2-0-and-claims/ /// <summary> /// Configures Umbraco to issue and process authentication tokens /// </summary> /// <param name="app"></param> /// <param name="authServerProviderOptions"></param> /// <remarks> /// This is a very simple implementation of token authentication, the expiry below is for a single day and with /// this implementation there is no way to force expire tokens on the server however given the code below and the additional /// callbacks that can be registered for the BackOfficeAuthServerProvider these types of things could be implemented. Additionally the /// BackOfficeAuthServerProvider could be overridden to include this functionality instead of coding the logic into the callbacks. /// </remarks> /// <example> /// /// An example of using this implementation is to use the UmbracoStandardOwinSetup and execute this extension method as follows: /// /// <![CDATA[ /// /// public override void Configuration(IAppBuilder app) /// { /// //ensure the default options are configured /// base.Configuration(app); /// /// //configure token auth /// app.UseUmbracoBackOfficeTokenAuth(); /// } /// /// ]]> /// /// Then be sure to read the details in UmbracoStandardOwinSetup on how to configure Owin to startup using it. /// </example> public static void UseUmbracoTokenAuthentication(this IAppBuilder app, UmbracoAuthorizationServerProviderOptions authServerProviderOptions = null) { authServerProviderOptions = authServerProviderOptions ?? new UmbracoAuthorizationServerProviderOptions(); //if a secret is supplied then var base64Key = Convert.ToBase64String( Encoding.UTF8.GetBytes( authServerProviderOptions.Secret)); var tokenProvider = new SymmetricKeyIssuerSecurityTokenProvider( AuthorizationPolicies.UmbracoRestApiIssuer, base64Key); var oAuthServerOptions = new OAuthAuthorizationServerOptions() { //generally you wouldn't allow this unless on SSL! AllowInsecureHttp = authServerProviderOptions.AllowInsecureHttp, TokenEndpointPath = new PathString(authServerProviderOptions.AuthEndpoint), AuthenticationType = AuthorizationPolicies.UmbracoRestApiTokenAuthenticationType, AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), Provider = new UmbracoAuthorizationServerProvider(authServerProviderOptions) }; oAuthServerOptions.AccessTokenFormat = new JwtFormatWriter( oAuthServerOptions, tokenProvider.Issuer, authServerProviderOptions.Audience, base64Key); // Token Generation app.UseOAuthAuthorizationServer(oAuthServerOptions); app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions { AllowedAudiences = new[] { authServerProviderOptions.Audience }, IssuerSecurityTokenProviders = new[] { tokenProvider }, Provider = new OAuthBearerAuthenticationProvider { OnApplyChallenge = context => { return(Task.FromResult(0)); }, OnRequestToken = context => { return(Task.FromResult(0)); }, OnValidateIdentity = context => { //ensure that the rest api claim is added to the ticket if everything is validated if (context.IsValidated) { context.Ticket.Identity.AddClaim(new Claim(AuthorizationPolicies.UmbracoRestApiClaimType, "true", ClaimValueTypes.Boolean, AuthorizationPolicies.UmbracoRestApiIssuer)); } return(Task.FromResult(0)); } } }); }
public async Task Get_Token() { var startup = new TestStartup( //This will be invoked before the controller is created so we can modify these mocked services, (testServices) => { }); var authServerOptions = new UmbracoAuthorizationServerProviderOptions { Secret = "abcdefghijklmnopqrstuvwxyz12345678909876543210", Audience = "test", AllowInsecureHttp = true }; using (var server = TestServer.Create(app => { ConfigureUserManager(app, new ClaimsIdentity(new[] { new Claim("test", "test") }), startup.ApplicationContext); app.UseUmbracoTokenAuthentication(authServerOptions); var httpConfig = startup.UseTestWebApiConfiguration(app); app.UseUmbracoRestApi(startup.ApplicationContext, new UmbracoRestApiOptions { //customize the authz policies, in this case we want to allow everything CustomAuthorizationPolicyCallback = (policyName, defaultPolicy) => (builder => builder.RequireAssertion(context => true)) }); app.UseWebApi(httpConfig); })) { var request = new HttpRequestMessage { RequestUri = new Uri($"http://testserver{authServerOptions.AuthEndpoint}"), Method = HttpMethod.Post, Content = new StringContent( "grant_type=password&username=YOURUSERNAME&password=YOURPASSWORD", Encoding.UTF8, "application/x-www-form-urlencoded") }; //add the origin so Cors kicks in! request.Headers.Add("Origin", "http://localhost:12061"); Console.WriteLine(request); var result = await server.HttpClient.SendAsync(request); Console.WriteLine(result); var json = await((StreamContent)result.Content).ReadAsStringAsync(); var djson = JsonConvert.DeserializeObject <JObject>(json); Console.Write(JsonConvert.SerializeObject(djson, Formatting.Indented)); Assert.AreEqual(HttpStatusCode.OK, result.StatusCode); Assert.IsTrue(result.Headers.Contains("Access-Control-Allow-Origin")); var acao = result.Headers.GetValues("Access-Control-Allow-Origin"); Assert.AreEqual(1, acao.Count()); //looks like the mvc cors default is to allow the request domain instea of * Assert.AreEqual("http://localhost:12061", acao.First()); Assert.IsNotNull(djson["access_token"].Value <string>()); Assert.AreEqual("bearer", djson["token_type"].Value <string>()); Assert.IsNotNull(djson["expires_in"].Value <string>()); } }
public async Task Use_Token() { var startup = new TestStartup( //This will be invoked before the controller is created so we can modify these mocked services, (testServices) => { }); var authServerOptions = new UmbracoAuthorizationServerProviderOptions { Secret = "abcdefghijklmnopqrstuvwxyz12345678909876543210", Audience = "test", AllowInsecureHttp = true }; using (var server = TestServer.Create(app => { ConfigureUserManager(app, //For published content we can have a normal ClaimsIdentity new ClaimsIdentity(new[] { new Claim(AuthorizationPolicies.UmbracoRestApiClaimType, "true", ClaimValueTypes.Boolean, AuthorizationPolicies.UmbracoRestApiIssuer), new Claim(ClaimTypes.Locality, "en-US", ClaimValueTypes.String, AuthorizationPolicies.UmbracoRestApiIssuer), new Claim(ClaimTypes.GivenName, "Admin", ClaimValueTypes.String, AuthorizationPolicies.UmbracoRestApiIssuer), new Claim(ClaimTypes.Name, "*****@*****.**", ClaimValueTypes.String, AuthorizationPolicies.UmbracoRestApiIssuer), new Claim(ClaimTypes.NameIdentifier, "0", ClaimValueTypes.Integer, AuthorizationPolicies.UmbracoRestApiIssuer), new Claim(Constants.Security.AllowedApplicationsClaimType, "content", ClaimValueTypes.String, AuthorizationPolicies.UmbracoRestApiIssuer), new Claim(Constants.Security.StartContentNodeIdClaimType, "[-1]", ClaimValueTypes.String, AuthorizationPolicies.UmbracoRestApiIssuer), new Claim(Constants.Security.StartMediaNodeIdClaimType, "[-1]", ClaimValueTypes.String, AuthorizationPolicies.UmbracoRestApiIssuer), }), startup.ApplicationContext); app.UseUmbracoTokenAuthentication(authServerOptions); var httpConfig = startup.UseTestWebApiConfiguration(app); app.UseUmbracoRestApi(startup.ApplicationContext, new UmbracoRestApiOptions { //customize the authz policies, in this case we want to allow everything CustomAuthorizationPolicyCallback = (policyName, defaultPolicy) => (builder => builder.RequireAssertion(context => true)) }); app.UseWebApi(httpConfig); })) { var tokenRequest = new HttpRequestMessage { RequestUri = new Uri($"http://testserver{authServerOptions.AuthEndpoint}"), Method = HttpMethod.Post, Content = new StringContent( "grant_type=password&username=YOURUSERNAME&password=YOURPASSWORD", Encoding.UTF8, "application/x-www-form-urlencoded") }; //add the origin so Cors kicks in! tokenRequest.Headers.Add("Origin", "http://localhost:12061"); Console.WriteLine(tokenRequest); var result = await server.HttpClient.SendAsync(tokenRequest); Console.WriteLine(result); var json = await((StreamContent)result.Content).ReadAsStringAsync(); var djson = JsonConvert.DeserializeObject <JObject>(json); Console.Write(JsonConvert.SerializeObject(djson, Formatting.Indented)); Assert.AreEqual(HttpStatusCode.OK, result.StatusCode); var token = djson["access_token"].Value <string>(); //Now we can use the token var accessRequest = new HttpRequestMessage { RequestUri = new Uri($"http://testserver/umbraco/rest/v1/{RouteConstants.ContentSegment}"), Method = HttpMethod.Get, Headers = { Authorization = new AuthenticationHeaderValue("Bearer", token) } }; accessRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/hal+json")); accessRequest.Headers.Add("Access-Control-Request-Headers", "accept, authorization"); accessRequest.Headers.Add("Access-Control-Request-Method", "GET"); accessRequest.Headers.Add("Origin", "http://localhost:12061"); accessRequest.Headers.Add("Referer", "http://localhost:12061/browser.html"); Console.WriteLine(accessRequest); result = await server.HttpClient.SendAsync(accessRequest); Console.WriteLine(result); json = await((StreamContent)result.Content).ReadAsStringAsync(); Console.Write(JsonConvert.SerializeObject(JsonConvert.DeserializeObject(json), Formatting.Indented)); Assert.AreEqual(HttpStatusCode.OK, result.StatusCode); } }