Во время некоторого тестирования с IdentityServer4 я обнаружил «странное» поведение.
Мой API не обнаруживает токены с истекшим сроком действия.
Настройка:
- Проект MVC, на котором размещен IdentiyServer4
- Проект API с одной конечной точкой теста
- Клиент cmd
На стороне IdentiyServer4 AccessTokenLifetime для моего клиента cmdустановлено на 20 секунд.
При регистрации в моем клиенте я вижу, что значение exp
токена JWT правильно установлено на now
+ 20 секунд.
API принимаетвсе звонки от клиента с токеном (включая токены с истекшим сроком действия).
Я ожидаю, что мой API будет принимать только действительные / еще не истекшие токены.Но это не так.Нужно ли включать / добавлять какой-либо компонент проверки в моем проекте API?
Вот конфигурация API:
public class Startup
{
public void Configure( IApplicationBuilder app, IHostingEnvironment env )
{
if ( env.IsDevelopment() )
app.UseDeveloperExceptionPage();
app.UseAuthentication();
app.UseMvc();
}
public void ConfigureServices( IServiceCollection services )
{
services.AddMvc();
services.AddAuthorization();
services
.AddAuthentication( IdentityServerAuthenticationDefaults.AuthenticationScheme )
.AddIdentityServerAuthentication( options =>
{
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.ApiName = "ApiPoc";
} );
}
}
Конфигурация IdentityServer-Host:
public class Startup
{
public void Configure( IApplicationBuilder app, IHostingEnvironment env )
{
if ( env.IsDevelopment() )
app.UseDeveloperExceptionPage();
app.UseIdentityServer();
}
public void ConfigureServices( IServiceCollection services )
{
// IdentityServer 4
services
.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryIdentityResources( Config.GetIdentityResources() ) //check below
.AddInMemoryApiResources( Config.GetApiResources() )
.AddInMemoryClients( Config.GetClients() )
.AddProfileService<ProfileService>();
// Test - custom user repo
services.AddTransient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>();
services.AddTransient<IProfileService, ProfileService>();
}
}
public class Config
{
public static IEnumerable<ApiResource> GetApiResources() =>
new List<ApiResource>
{
new ApiResource( "ApiPoc", "API PoC" )
};
public static IEnumerable<Client> GetClients() =>
new List<Client>
{
new Client
{
ClientId = "CmdClient",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets =
{
new Secret( "CmdClientSecret".Sha256() )
},
AllowedScopes = { "ApiPoc" },
AccessTokenLifetime = 20,
AccessTokenType = AccessTokenType.Jwt,
SlidingRefreshTokenLifetime = 1296000
}
};
public static IEnumerable<IdentityResource> GetIdentityResources() =>
new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile()
};
}
Код клиента Cmd:
private static async Task DoRequestInLoop()
{
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync( "http://localhost:5000" ).ConfigureAwait( false );
if ( disco.IsError )
{
Console.WriteLine( $"Failed to get discovery document: {disco.Error}" );
return;
}
var tokenResponse = await client.RequestPasswordTokenAsync( new PasswordTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = "CmdClient",
ClientSecret = "CmdClientSecret",
Scope = "ApiPoc",
Password = "user",
UserName = "password",
Parameters = new Dictionary<String, String>
{
{ "UserGroup", "A_Admin" },
{ "Tenant", "A" }
}
},
CancellationToken.None ).ConfigureAwait( false );
if ( tokenResponse.IsError )
{
Console.WriteLine( $"Failed to obtain token: {tokenResponse.Error}." );
return;
}
var json = tokenResponse.Json.ToString();
client.SetBearerToken( tokenResponse.AccessToken );
Console.WriteLine( $"Token: {json}" );
while ( !_loopCanceled )
{
var response = await client.GetAsync( "http://localhost:5001/api/Values" ).ConfigureAwait( false );
Console.WriteLine( !response.IsSuccessStatusCode ? $"Request failed with status code {response.StatusCode}" : "Request successful." );
await Task.Delay( 22000 ).ConfigureAwait( false );
}
}
Токен, полученный клиентом от IdentityServer (вы можете видеть, что он действителен только в течение 20 секунд, что правильно)
{
"nbf": 1546243372,
"exp": 1546243392,
"iss": "http://localhost:5000",
"aud": [
"http://localhost:5000/resources",
"ApiPoc"
],
"client_id": "CmdClient",
"sub": "1",
"auth_time": 1546243372,
"idp": "local",
"SomeClaim": "Claim",
"scope": [
"ApiPoc"
],
"amr": [
"custom"
]
}