public async Task UpdateByDeviceCodeAsync() { var deviceCode = await _deviceFlowStore.FindByDeviceCodeAsync("DeviceCode1"); deviceCode.ShouldNotBeNull(); deviceCode.Lifetime.ShouldBe(42); await _deviceFlowStore.UpdateByUserCodeAsync( "DeviceFlowCodesUserCode1", new DeviceCode { Lifetime = 43 } ); deviceCode = await _deviceFlowStore.FindByDeviceCodeAsync("DeviceCode1"); deviceCode.Lifetime.ShouldBe(43); }
/// <summary> /// Updates device authorization, searching by user code. /// </summary> /// <param name="userCode">The user code.</param> /// <param name="data">The data.</param> /// <returns></returns> public Task UpdateByUserCodeAsync(string userCode, DeviceCode data) { return(_store.UpdateByUserCodeAsync(userCode.Sha256(), data)); }
/// <summary> /// Updates device authorization, searching by user code. /// </summary> /// <param name="userCode">The user code.</param> /// <param name="data">The data.</param> /// <returns></returns> public Task UpdateByUserCodeAsync(string userCode, DeviceCode data) { using var activity = Tracing.StoreActivitySource.StartActivity("DefaultDeviceFlowCodeService.UpdateByUserCode"); return(_store.UpdateByUserCodeAsync(userCode.Sha256(), data)); }
public async Task UpdateByUserCode(string userCode, [FromBody] DeviceCode data) { await _store.UpdateByUserCodeAsync(userCode, data); }
public async Task <IActionResult> AuthorizeDeviceAsync([FromBody] AuthorizeDeviceRequest data) { // validate client var clientResult = await _clientValidator.ValidateAsync(this.HttpContext); if (clientResult.Client == null) { return(Unauthorized(OidcConstants.TokenErrors.InvalidClient)); } var client = clientResult.Client as ClientExtra; var grantType = clientResult.Client.AllowedGrantTypes.FirstOrDefault(agt => agt == OidcConstants.GrantTypes.DeviceCode); if (grantType == null) { return(Unauthorized(OidcConstants.TokenErrors.InvalidGrant)); } var deviceAuth = await _deviceFlowStore.FindByUserCodeAsync(data.UserCode.Sha256()); if (deviceAuth == null) { return(NotFound($"Invalid user code, Device authorization failure - user code is invalid")); } // VALIDATE if issuer must exist and must be allowed // ------------------------------------------------------------------- var issuer = data.Issuer; if (!string.IsNullOrEmpty(data.Issuer)) { issuer = issuer.ToLower(); var foundIssuer = client.AllowedArbitraryIssuers.FirstOrDefault(x => x == issuer); if (string.IsNullOrWhiteSpace(foundIssuer)) { return(NotFound($"issuer:{issuer} is NOT in the AllowedArbitraryIssuers collection.")); } } // VALIDATE if AccessTokenLifetime must exist and must be allowed // ------------------------------------------------------------------- int lifetime = client.AccessTokenLifetime; if (data.AccessTokenLifetime != null) { var requestedAccessTokenLifetime = (int)data.AccessTokenLifetime; if (requestedAccessTokenLifetime > 0 && requestedAccessTokenLifetime <= client.AccessTokenLifetime) { lifetime = requestedAccessTokenLifetime; } else { return(NotFound($"AccessTokenLifetime:{requestedAccessTokenLifetime} is NOT in range 0-{client.AccessTokenLifetime}.")); } } if (data.AccessTokenLifetime != null) { deviceAuth.Lifetime = (int)data.AccessTokenLifetime; } string subject = ""; try { var validatedResult = await _identityTokenValidator.ValidateIdTokenAsync(data.IdToken, _tokenExchangeOptions.AuthorityKey); if (validatedResult.IsError) { throw new Exception( $"failed to validate: {FluffyBunny4.Constants.TokenExchangeTypes.SubjectToken}={data.IdToken}", new Exception(validatedResult.Error)); } subject = SubjectFromClaimsPrincipal(validatedResult.User); if (string.IsNullOrWhiteSpace(subject)) { throw new Exception( $"subject does not exist: {FluffyBunny4.Constants.TokenExchangeTypes.SubjectToken}={data.IdToken}"); } var claims = validatedResult.User.Claims.ToList(); claims.Add(new Claim(JwtClaimTypes.AuthenticationTime, claims.FirstOrDefault(c => c.Type == JwtClaimTypes.IssuedAt).Value)); claims.Add(new Claim(JwtClaimTypes.IdentityProvider, _tokenExchangeOptions.AuthorityKey)); var newClaimsIdentity = new ClaimsIdentity(claims); var subjectPrincipal = new ClaimsPrincipal(newClaimsIdentity); deviceAuth.Subject = subjectPrincipal; var requestedScopes = deviceAuth.RequestedScopes.ToList(); var offlineAccess = requestedScopes.FirstOrDefault(x => x == IdentityServerConstants.StandardScopes.OfflineAccess); var allowedScopes = new List <string>(); var allowedClaims = new List <Claim>(); var finalCustomPayload = new Dictionary <string, object>(); var consentAuthorizeResponseTasks = new List <Task <ConsentAuthorizeResponseContainer <PostContext> > >(); var requestedServiceScopes = GetServiceToScopesFromRequest(deviceAuth.RequestedScopes.ToList()); foreach (var serviceScopeSet in requestedServiceScopes) { var externalService = await _externalServicesStore.GetExternalServiceByNameAsync(serviceScopeSet.Key); if (externalService == null) { continue; } var discoCache = await _consentDiscoveryCacheAccessor.GetConsentDiscoveryCacheAsync(serviceScopeSet.Key); var doco = await discoCache.GetAsync(); if (doco.IsError) { _logger.LogError(doco.Error); continue; } List <string> scopes = null; switch (doco.AuthorizationType) { case Constants.AuthorizationTypes.Implicit: scopes = null; break; case Constants.AuthorizationTypes.SubjectAndScopes: scopes = serviceScopeSet.Value; break; } if (doco.AuthorizationType == Constants.AuthorizationTypes.Implicit) { allowedScopes.AddRange(serviceScopeSet.Value); } else { var request = new ConsentAuthorizeRequest { AuthorizeType = doco.AuthorizationType, Scopes = scopes, Subject = subject, Requester = new ConsentAuthorizeRequest.ClientRequester() { ClientDescription = client.Description, ClientId = client.ClientId, ClientName = client.ClientName, Namespace = client.Namespace, Tenant = client.TenantName } }; // How to send many requests in parallel in ASP.Net Core // https://www.michalbialecki.com/2018/04/19/how-to-send-many-requests-in-parallel-in-asp-net-core/ var task = _consentExternalService.PostAuthorizationRequestAsync(doco, request, new PostContext() { ServiceScopeSet = serviceScopeSet, DiscoveryDocument = doco }); consentAuthorizeResponseTasks.Add(task); } } var consentAuthorizeResponses = await Task.WhenAll(consentAuthorizeResponseTasks); foreach (var consentAuthorizeResponse in consentAuthorizeResponses) { var response = consentAuthorizeResponse.Response; var serviceScopeSet = consentAuthorizeResponse.Context.ServiceScopeSet; var doco = consentAuthorizeResponse.Context.DiscoveryDocument; if (response.Error != null) { _logger.LogError(response.Error.Message); continue; } if (response.Authorized) { switch (doco.AuthorizationType) { case Constants.AuthorizationTypes.SubjectAndScopes: // make sure no funny business is coming in from the auth call. var serviceRoot = $"{_tokenExchangeOptions.BaseScope}{serviceScopeSet.Key}"; var query = (from item in response.Scopes where item.StartsWith(serviceRoot) select item); allowedScopes.AddRange(query); if (response.Claims != null && response.Claims.Any()) { foreach (var cac in response.Claims) { // namespace the claims. allowedClaims.Add(new Claim($"{serviceScopeSet.Key}.{cac.Type}", cac.Value)); } } if (response.CustomPayload != null) { finalCustomPayload.Add(serviceScopeSet.Key, response.CustomPayload); } break; } } } if (finalCustomPayload.Any()) { allowedClaims.Add(new Claim( Constants.CustomPayload, _serializer.Serialize(finalCustomPayload), IdentityServerConstants.ClaimValueTypes.Json)); } deviceAuth.IsAuthorized = true; // deviceAuth.SessionId = sid; if (!string.IsNullOrWhiteSpace(offlineAccess)) { allowedScopes.Add(offlineAccess); } deviceAuth.AuthorizedScopes = allowedScopes; var deviceExtra = _coreMapperAccessor.Mapper.Map <DeviceCodeExtra>(deviceAuth); deviceExtra.AuthorizedClaims = allowedClaims; deviceExtra.Issuer = issuer; deviceExtra.AccessTokenLifetime = lifetime; await _deviceFlowStore.UpdateByUserCodeAsync(data.UserCode.Sha256(), deviceExtra); return(Ok()); } catch (Exception ex) { _logger.LogError(ex, ex.Message); return(StatusCode(500)); } }