-
Notifications
You must be signed in to change notification settings - Fork 32
Expand file tree
/
Copy pathMoryxIdentityHandler.cs
More file actions
105 lines (93 loc) · 4.61 KB
/
Copy pathMoryxIdentityHandler.cs
File metadata and controls
105 lines (93 loc) · 4.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// Copyright (c) 2026 Phoenix Contact GmbH & Co. KG
// Licensed under the Apache License, Version 2.0
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Moryx.Identity;
/// <summary>
/// An <see cref="AuthenticationHandler{TOptions}"/> that can perform MORYX Identity based authentication.
/// </summary>
public class MoryxIdentityHandler : AuthenticationHandler<MoryxIdentityOptions>
{
private readonly JwtSecurityTokenHandler _jwtTokenHandler;
private readonly MoryxAccessManagementClient _client;
/// <summary>
/// Initializes a new instance of <see cref="MoryxIdentityHandler"/>.
/// </summary>
/// <inheritdoc />
public MoryxIdentityHandler(
IOptionsMonitor<MoryxIdentityOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock,
IMemoryCache memoryCache,
HttpClient identityClient = null)
: base(options, logger, encoder, clock)
{
_jwtTokenHandler = new JwtSecurityTokenHandler();
_client = new MoryxAccessManagementClient(options, memoryCache, logger.CreateLogger(nameof(MoryxIdentityHandler) + ":" + nameof(MoryxAccessManagementClient)), identityClient);
}
/// <summary>
/// Searches the 'Authorization' header for a Cookie with the name specified in <see cref="MoryxIdentityOptions.CookieName"/>.
/// If the cookie is found, it is validated using the MORYX Identity Server endpoint specified in the <see cref="MoryxIdentityOptions"/>.
/// </summary>
/// <returns></returns>
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
var metadata = Context.GetEndpoint()?.Metadata;
var authorizeAttributes = metadata?.OfType<AuthorizeAttribute>();
if (authorizeAttributes?.Any() != true || metadata.GetMetadata<IAllowAnonymous>() != null)
return AuthenticateResult.NoResult(); // endpoint has AllowAnonymous or no Authorize attribute
if (Context.User == null)
return AuthenticateResult.Fail("User not found.");
var token = Context.Request.Cookies[MoryxIdentityDefaults.JWT_COOKIE_NAME];
if (token == null)
return AuthenticateResult.Fail("JwtToken not found");
var refreshToken = Context.Request.Cookies[MoryxIdentityDefaults.REFRESH_TOKEN_COOKIE_NAME];
if (refreshToken == null)
return AuthenticateResult.Fail("RefreshToken not found");
var decodedJwt = _jwtTokenHandler.ReadJwtToken(token);
if (decodedJwt.ValidTo < DateTime.UtcNow)
{
var refreshResult = await _client.GetRefreshedTokensAsync(token, refreshToken);
if (refreshResult is null || !refreshResult.Success)
return AuthenticateResult.Fail("Token is expired and could not be refreshed");
token = refreshResult.Token;
refreshToken = refreshResult.RefreshToken;
Context.Response.Cookies.Append(MoryxIdentityDefaults.JWT_COOKIE_NAME, token, new CookieOptions
{
HttpOnly = true,
Secure = true,
SameSite = SameSiteMode.Lax,
Domain = refreshResult.Domain,
});
Context.Response.Cookies.Append(MoryxIdentityDefaults.REFRESH_TOKEN_COOKIE_NAME, refreshToken, new CookieOptions
{
HttpOnly = true,
Secure = true,
SameSite = SameSiteMode.Lax,
Domain = refreshResult.Domain,
});
}
// if multiple policies are required set filter to empty string
string requiredPolicy = "";
var distinctPolicies = authorizeAttributes.Select(a => a.Policy).Where(s => !string.IsNullOrEmpty(s)).Distinct();
if (distinctPolicies.Count() == 1)
requiredPolicy = distinctPolicies.First();
var permissions = await _client.GetPermissionsAsync(token, refreshToken, requiredPolicy);
if (permissions == null)
return AuthenticateResult.Fail("Retrieving Permissions failed");
var appIdentity = new ClaimsIdentity(MoryxIdentityOptions.MoryxIdentity);
foreach (var perm in permissions)
appIdentity.AddClaim(new Claim("Permission", perm));
var ticket = new AuthenticationTicket(
new ClaimsPrincipal(appIdentity), this.Scheme.Name);
return AuthenticateResult.Success(ticket);
}
}