/// <summary> /// Determine if the user's scope claim (if any) matches the URL and HTTP operation they are attempting to carry out /// </summary> /// <param name="context">Contains authorization information used by Microsoft.AspNetCore.Authorization.IAuthorizationHandler</param> /// <param name="requirement">Information about what the requirement for this HTTP operation are</param> /// <returns>Task</returns> protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, HasScopeRequirement requirement) { // Let's see if the resource is an auth filter. If not, exit var resource = (context.Resource as Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext); if (resource == null || !resource.RouteData.Values.Keys.Contains("db") || !resource.RouteData.Values.Keys.Contains("collection")) { return(Task.CompletedTask); } var parts = requirement.GetScopeParts(); string systemName = parts.SystemName; string serviceName = parts.ServiceName; string permission = parts.Permission; /* We need to get the dot-separated path to the collection, such as fdns.object.bookstore.customer. This dot-separated * path is mapped to an HTTP route: "object" is the name of the servce (the Object microservice), "bookstore" is * the database name, and "customer" is the collection, e.g. /api/1.0/bookstore/customer. Before we can authorize * the user we have to build that dot-separated list so we can compare it to one of the scopes that was passed in * via the OAuth2 token. The first step is to get the dot-separated list from the URL, and then we add the * create/read/update/delete/etc portion at the end, per the requirement passed into the method call. */ var scopeFromRoute = GetScopeFromRoute(resource); string requiredScope = $"{scopeFromRoute.Scope}.{permission}"; if (!serviceName.Equals(scopeFromRoute.ScopeParts[1]) || !SystemName.Equals(scopeFromRoute.ScopeParts[0])) { // the scope doesn't include the proper service name or system name context.Fail(); return(Task.CompletedTask); } // Just a check to see if the user identity object has a scope claim. If not, something is wrong and exit if (!context.User.HasClaim(c => c.Type == SCOPE && c.Issuer == requirement.Issuer)) { context.Fail(); return(Task.CompletedTask); } /* Let's figure out all the scopes the user has been authorized to. These came from the OAuth2 token and have been * parsed by the ASP.NET Core middleware. We just an array of strings for simplicity's sake. */ var tokenScopes = context.User.FindFirst(c => c.Type == SCOPE && c.Issuer == requirement.Issuer).Value.Split(' '); // Succeed if the scope array contains the required scope if (HasScope(requiredScope, tokenScopes)) { context.Succeed(requirement); } else { context.Fail(); } return(Task.CompletedTask); }
/// <summary> /// Determine if the user's scope claim (if any) matches the URL and HTTP operation they are attempting to carry out /// </summary> /// <param name="context">Contains authorization information used by Microsoft.AspNetCore.Authorization.IAuthorizationHandler</param> /// <param name="requirement">Information about what the requirement for this HTTP operation are</param> /// <returns>Task</returns> protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, HasScopeRequirement requirement) { // Let's see if the resource is an auth filter. If not, exit var resource = (context.Resource as Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext); if (resource == null || !resource.RouteData.Values.Keys.Contains("db") || !resource.RouteData.Values.Keys.Contains("collection")) { return; } var parts = requirement.GetScopeParts(); string systemName = parts.SystemName; string serviceName = parts.ServiceName; string permission = parts.Permission; /* We need to get the dot-separated path to the collection, such as fdns.object.bookstore.customer. This dot-separated * path is mapped to an HTTP route: "object" is the name of the servce (the Object microservice), "bookstore" is * the database name, and "customer" is the collection, e.g. /api/1.0/bookstore/customer. Before we can authorize * the user we have to build that dot-separated list so we can compare it to one of the scopes that was passed in * via the OAuth2 token. The first step is to get the dot-separated list from the URL, and then we add the * create/read/update/delete/etc portion at the end, per the requirement passed into the method call. */ var scopeFromRoute = GetScopeFromRoute(resource); string requiredScope = $"{scopeFromRoute.Scope}.{permission}"; if (!serviceName.Equals(scopeFromRoute.ScopeParts[1]) || !SystemName.Equals(scopeFromRoute.ScopeParts[0])) { // the scope doesn't include the proper service name or system name context.Fail(); return; } string responseBody = string.Empty; string token = resource.HttpContext.Request.Headers["Authorization"]; using (HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Post, _introspectionUri)) { var formData = new Dictionary <string, string>(); formData.Add("token", token); message.Content = new FormUrlEncodedContent(formData); using (var response = await _client.SendAsync(message)) { responseBody = await response.Content.ReadAsStringAsync(); } } IntrospectionResponse introspectionResponse = JsonConvert.DeserializeObject <IntrospectionResponse>(responseBody); if (!introspectionResponse.Active || !introspectionResponse.Exp.HasValue) { context.Fail(); return; } DateTimeOffset expirationTimeOffset = DateTimeOffset.FromUnixTimeSeconds(introspectionResponse.Exp.Value); DateTime expirationTime = expirationTimeOffset.UtcDateTime; DateTime now = DateTime.Now; if (now > expirationTime) { // fail due to expiration context.Fail(); return; } if (HasScope(requiredScope, introspectionResponse.Scopes)) { context.Succeed(requirement); return; } context.Fail(); }