From 9c940d354d49a0075863e547c67dd49fab3613c8 Mon Sep 17 00:00:00 2001 From: Dominick Baier Date: Sun, 15 Jan 2017 13:15:44 +0100 Subject: [PATCH] return id_token on refresh token request (see #690) --- .../TokenResponseGenerator.cs | 21 ++++++- .../Clients/RefreshTokenClient.cs | 58 +++++++++++++++++++ 2 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 test/IdentityServer.IntegrationTests/Clients/RefreshTokenClient.cs diff --git a/src/IdentityServer4/ResponseHandling/TokenResponseGenerator.cs b/src/IdentityServer4/ResponseHandling/TokenResponseGenerator.cs index c5cfe9f879..f90f41af21 100644 --- a/src/IdentityServer4/ResponseHandling/TokenResponseGenerator.cs +++ b/src/IdentityServer4/ResponseHandling/TokenResponseGenerator.cs @@ -22,7 +22,7 @@ public class TokenResponseGenerator : ITokenResponseGenerator private readonly IRefreshTokenService _refreshTokenService; private readonly IResourceStore _resources; private readonly IClientStore _clients; - + public TokenResponseGenerator(ITokenService tokenService, IRefreshTokenService refreshTokenService, IResourceStore resources, IClientStore clients, ILoggerFactory loggerFactory) { _tokenService = tokenService; @@ -133,7 +133,7 @@ private async Task ProcessRefreshTokenRequestAsync(ValidatedToken var oldAccessToken = request.RefreshToken.AccessToken; string accessTokenString; - + if (request.Client.UpdateAccessTokenClaimsOnRefresh) { var subject = request.RefreshToken.Subject; @@ -161,6 +161,7 @@ private async Task ProcessRefreshTokenRequestAsync(ValidatedToken return new TokenResponse { + IdentityToken = await CreateIdTokenFromRefreshTokenRequestAsync(request, accessTokenString), AccessToken = accessTokenString, AccessTokenLifetime = request.Client.AccessTokenLifetime, RefreshToken = handle @@ -221,5 +222,21 @@ private async Task> CreateAccessTokenAsync(ValidatedTokenR var securityToken = await _tokenService.CreateSecurityTokenAsync(accessToken); return Tuple.Create(securityToken, refreshToken); } + + private async Task CreateIdTokenFromRefreshTokenRequestAsync(ValidatedTokenRequest request, string newAccessToken) + { + var oldAccessToken = request.RefreshToken.AccessToken; + var tokenRequest = new TokenCreationRequest + { + Subject = request.RefreshToken.Subject, + Client = request.Client, + Resources = await _resources.FindEnabledResourcesByScopeAsync(oldAccessToken.Scopes), + ValidatedRequest = request, + AccessTokenToHash = newAccessToken + }; + + var idToken = await _tokenService.CreateIdentityTokenAsync(tokenRequest); + return await _tokenService.CreateSecurityTokenAsync(idToken); + } } } \ No newline at end of file diff --git a/test/IdentityServer.IntegrationTests/Clients/RefreshTokenClient.cs b/test/IdentityServer.IntegrationTests/Clients/RefreshTokenClient.cs new file mode 100644 index 0000000000..f04bb8c108 --- /dev/null +++ b/test/IdentityServer.IntegrationTests/Clients/RefreshTokenClient.cs @@ -0,0 +1,58 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +using FluentAssertions; +using IdentityModel.Client; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.TestHost; +using System.Net.Http; +using System.Threading.Tasks; +using Xunit; + +namespace IdentityServer4.IntegrationTests.Clients +{ + public class RefreshTokenClient + { + const string TokenEndpoint = "https://server/connect/token"; + + private readonly HttpClient _client; + private readonly HttpMessageHandler _handler; + + public RefreshTokenClient() + { + var builder = new WebHostBuilder() + .UseStartup(); + var server = new TestServer(builder); + + _handler = server.CreateHandler(); + _client = server.CreateClient(); + } + + [Fact] + public async Task requesting_a_refresh_token_should_return_expected_results() + { + var client = new TokenClient( + TokenEndpoint, + "roclient", + "secret", + innerHttpMessageHandler: _handler); + + var response = await client.RequestResourceOwnerPasswordAsync("bob", "bob", "api1 offline_access"); + + response.IsError.Should().BeFalse(); + response.ExpiresIn.Should().Be(3600); + response.TokenType.Should().Be("Bearer"); + response.IdentityToken.Should().BeNull(); + response.RefreshToken.Should().NotBeNull(); + + response = await client.RequestRefreshTokenAsync(response.RefreshToken); + + response.IsError.Should().BeFalse(); + response.ExpiresIn.Should().Be(3600); + response.TokenType.Should().Be("Bearer"); + response.IdentityToken.Should().NotBeNull(); + response.RefreshToken.Should().NotBeNull(); + } + } +} \ No newline at end of file