public override async Task <ResourceValidationResult> ValidateRequestedResourcesAsync(ResourceValidationRequest request) { var nvc = _scopedHttpContextRequestForm.GetFormCollection(); var token = nvc["token"]; if (string.IsNullOrWhiteSpace(token)) { token = nvc["refresh_token"]; } if (!string.IsNullOrWhiteSpace(token)) { if (token.StartsWith("1_")) { // this has already been validated. if (request == null) { throw new ArgumentNullException(nameof(request)); } var result = new ResourceValidationResult(); var parsedScopesResult = _scopeParser.ParseScopeValues(request.Scopes); result.ParsedScopes = parsedScopesResult.ParsedScopes; return(result); } } return(await base.ValidateRequestedResourcesAsync(request)); }
protected async override Task <(string accessToken, string refreshToken)> CreateAccessTokenAsync(ValidatedTokenRequest request) { var formCollection = _scopedHttpContextRequestForm.GetFormCollection(); var grantType = formCollection["grant_type"]; var tokenRequest = new TokenCreationRequest { Subject = request.Subject, ValidatedResources = request.ValidatedResources, ValidatedRequest = request }; bool createRefreshToken; var authorizedScopes = Enumerable.Empty <string>(); IEnumerable <string> authorizedResourceIndicators = null; if (request.AuthorizationCode != null) { createRefreshToken = request.ValidatedResources.Resources.OfflineAccess; // createRefreshToken = request.AuthorizationCode.RequestedScopes.Contains(IdentityServerConstants.StandardScopes.OfflineAccess); // load the client that belongs to the authorization code Client client = null; if (request.AuthorizationCode.ClientId != null) { // todo: do we need this check? client = await Clients.FindEnabledClientByIdAsync(request.AuthorizationCode.ClientId); } if (client == null) { throw new InvalidOperationException("Client does not exist anymore."); } tokenRequest.Subject = request.AuthorizationCode.Subject; tokenRequest.Description = request.AuthorizationCode.Description; authorizedScopes = request.AuthorizationCode.RequestedScopes; authorizedResourceIndicators = request.AuthorizationCode.RequestedResourceIndicators; } else if (request.DeviceCode != null) { createRefreshToken = request.DeviceCode.AuthorizedScopes.Contains(IdentityServerConstants.StandardScopes.OfflineAccess); Client client = null; if (request.DeviceCode.ClientId != null) { // todo: do we need this check? client = await Clients.FindEnabledClientByIdAsync(request.DeviceCode.ClientId); } if (client == null) { throw new InvalidOperationException("Client does not exist anymore."); } tokenRequest.Subject = request.DeviceCode.Subject; tokenRequest.Description = request.DeviceCode.Description; authorizedScopes = request.DeviceCode.AuthorizedScopes; } else { createRefreshToken = request.RequestedScopes.Contains(IdentityServerConstants.StandardScopes.OfflineAccess); authorizedScopes = request.ValidatedResources.RawScopeValues; } var at = await TokenService.CreateAccessTokenAsync(tokenRequest); object obj; if (_scopedStorage.TryGetValue(Constants.ScopedRequestType.OverrideTokenIssuedAtTime, out obj)) { DateTime issuedAtTime = obj is DateTime ? (DateTime)obj : default; at.CreationTime = issuedAtTime; } var finalScopes = at.Scopes.ToList(); if (createRefreshToken) { if (!finalScopes.Contains(IdentityServerConstants.StandardScopes.OfflineAccess)) { finalScopes.Add(IdentityServerConstants.StandardScopes.OfflineAccess); } } else { if (finalScopes.Contains(IdentityServerConstants.StandardScopes.OfflineAccess)) { finalScopes.Remove(IdentityServerConstants.StandardScopes.OfflineAccess); } } var accessToken = await TokenService.CreateSecurityTokenAsync(at); string refreshToken = null; if (createRefreshToken) { var rtRequest = new RefreshTokenCreationRequest { Client = request.Client, Subject = tokenRequest.Subject, Description = tokenRequest.Description, AuthorizedScopes = authorizedScopes, AuthorizedResourceIndicators = authorizedResourceIndicators, AccessToken = at, RequestedResourceIndicator = request.RequestedResourceIndicator, }; refreshToken = await RefreshTokenService.CreateRefreshTokenAsync(rtRequest); } if (_scopedStorage.TryGetValue(Constants.ScopedRequestType.ExtensionGrantValidationContext, out obj)) { var extensionGrantValidationContext = obj as ExtensionGrantValidationContext; if (extensionGrantValidationContext.Request.GrantType == FluffyBunny4.Constants.GrantType.TokenExchangeMutate) { var refreshTokenStoreGrantStoreHashAccessor = _refreshTokenStore as IGrantStoreHashAccessor; var referenceTokenStoreGrantStoreHashAccessor = _referenceTokenStore as IGrantStoreHashAccessor; _scopedStorage.TryGetValue(Constants.ScopedRequestType.PersistedGrantExtra, out obj); var persistedGrantExtra = _scopedStorage.Get <PersistedGrantExtra>(Constants.ScopedRequestType.PersistedGrantExtra); var subjectToken = _scopedStorage.Get <string>(Constants.ScopedRequestType.SubjectToken); var fixedSubjectToken = subjectToken.Substring(2); var newKey = referenceTokenStoreGrantStoreHashAccessor.GetHashedKey(accessToken); var originalKey = referenceTokenStoreGrantStoreHashAccessor.GetHashedKey(fixedSubjectToken); await _persistedGrantStore.CopyAsync(newKey, originalKey); await _persistedGrantStore.RemoveAsync(newKey); if (!createRefreshToken && !string.IsNullOrWhiteSpace(persistedGrantExtra.RefreshTokenKey)) { // need to kill the old refresh token, as this mutate didn't ask for a new one. await _persistedGrantStore.RemoveAsync(persistedGrantExtra.RefreshTokenKey); } else { newKey = refreshTokenStoreGrantStoreHashAccessor.GetHashedKey(refreshToken); await _persistedGrantStore.CopyAsync(newKey, persistedGrantExtra.RefreshTokenKey); await _persistedGrantStore.RemoveAsync(newKey); } // we need to point the old access_token and refresh_token to this new set; return(subjectToken, null); // mutate doesn't get to get the refresh_token back, the original holder of it is the only owner. } } switch (grantType) { case FluffyBunny4.Constants.GrantType.ArbitraryIdentity: case FluffyBunny4.Constants.GrantType.ArbitraryToken: case OidcConstants.GrantTypes.DeviceCode: case FluffyBunny4.Constants.GrantType.TokenExchange: case FluffyBunny4.Constants.GrantType.TokenExchangeMutate: if (!accessToken.Contains('.')) { accessToken = $"1_{accessToken}"; } if (!string.IsNullOrWhiteSpace(refreshToken)) { refreshToken = $"1_{refreshToken}"; } break; default: if (!accessToken.Contains('.')) { accessToken = $"0_{accessToken}"; } if (!string.IsNullOrWhiteSpace(refreshToken)) { refreshToken = $"0_{refreshToken}"; } break; } return(accessToken, refreshToken); // return (accessToken, null); }
public async Task StoreAsync(PersistedGrant grant) { var context = GetTenantContext(); var token = grant as FluffyBunny4.Models.PersistedGrantExtra; var grantType = _scopedHttpContextRequestForm.GetFormCollection()["grant_type"]; if (grantType == "refresh_token") { if (grant.Type != IdentityServerConstants.PersistedGrantTypes.RefreshToken) { // refresh_token coming. so save this access_token for later storage _scopedStorage.AddOrUpdate(Constants.ScopedRequestType.AccessTokenPersistedGrant, grant); return; // store access_token for later } else { object obj; _scopedStorage.TryGetValue(Constants.ScopedRequestType.AccessTokenPersistedGrant, out obj); if (obj != null) { var grantStored = obj as Duende.IdentityServer.Models.PersistedGrant; var extra = _entityFrameworkMapperAccessor.MapperOneToOne .Map <FluffyBunny4.Models.PersistedGrantExtra>(grantStored); extra.RefreshTokenKey = grant.Key; await InnerStoreAsync(extra); await InnerStoreAsync(grant); _scopedStorage.TryRemove(Constants.ScopedRequestType.AccessTokenPersistedGrant, out obj); } else { // the persisted grant before this was NOT a reference token, so we will assume it was a JWT await InnerStoreAsync(grant); } return; } } else { object obj; if (_scopedStorage.TryGetValue(Constants.ScopedRequestType.ExtensionGrantValidationContext, out obj)) { var extensionGrantValidationContext = obj as ExtensionGrantValidationContext; if (extensionGrantValidationContext.Request.GrantType == FluffyBunny4.Constants.GrantType.TokenExchange || extensionGrantValidationContext.Request.GrantType == FluffyBunny4.Constants.GrantType.TokenExchangeMutate) { if (grant.Type != IdentityServerConstants.PersistedGrantTypes.RefreshToken) { var offlineAccess = extensionGrantValidationContext.Request.RequestedScopes.FirstOrDefault(scope => scope == IdentityServerConstants.StandardScopes.OfflineAccess); if (offlineAccess != null) { // refresh_token coming. so save this access_token for later storage _scopedStorage.AddOrUpdate(Constants.ScopedRequestType.AccessTokenPersistedGrant, grant); return; } // store access_token for later } else { _scopedStorage.TryGetValue(Constants.ScopedRequestType.AccessTokenPersistedGrant, out obj); if (obj != null) { var grantStored = obj as Duende.IdentityServer.Models.PersistedGrant; var extra = _entityFrameworkMapperAccessor.MapperOneToOne .Map <FluffyBunny4.Models.PersistedGrantExtra>(grantStored); extra.RefreshTokenKey = grant.Key; await InnerStoreAsync(extra); await InnerStoreAsync(grant); _scopedStorage.TryRemove(Constants.ScopedRequestType.AccessTokenPersistedGrant, out obj); } else { // the persisted grant before this was NOT a reference token, so we will assume it was a JWT await InnerStoreAsync(grant); } //_scopedStorage.TryRemove(Constants.ScopedRequestType.AccessTokenPersistedGrant, out obj); // _scopedStorage.TryRemove(Constants.ScopedRequestType.ExtensionGrantValidationContext, out obj); return; } } } } await InnerStoreAsync(grant); }