Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OAuth parse token array-type claims #16

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Frends.OAuth.ParseToken/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## [1.2.0] - 2024-08-08
### Added
- Support for handling array-type claims in the token, such as `"roles": ["editor", "admin"]`.

## [1.1.0] - 2023-12-08
### Added
- New dictionary parameter 'ParseResult.Claims' that contains all claims in the token.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImM2NjNiOWRjLWUwZWYtNDllMC04MjAxLWU3Y2FlODMyNmJkMiJ9.eyJuYW1lIjoiRnJlbmRzIFRlc3QiLCJhdWQiOiJmSVZMb3VLVVppaFhmWVAzdGRPOUQzZHdkNlpOUzlCZSIsImlzcyI6ImZyZW5kcyIsImlhdCI6MTUxNjIzOTAyMiwidGVzdF9hcnJheSI6WyJpdGVtMSIsIml0ZW0yIiwiaXRlbTMiLCJpdGVtNCJdfQ.rhsLtBjFHY-63pi9YKJKP0e4vIkKKs5wr3xok8iBYZCM4Juklr56SBW5XKYCaII5HMmVYDrQUpPlSBZBYQYcWlizJPnysBB3idswoW6RJQm6jfRkWvicWSIQSSOGQmiVtC2yRBUOX-gd42Q4uKad31QJSTZ3mLjHqaJqqdeBLImSSGjOx8S1XiTdoFuGLPhhAotrtHN2mFcgJITvVqJcPFCAWrIw7GMerznmrlQ86W5beZ9nwLpfZfPCOWuO4_DcE_iTEYVy7nWzWOquubPqQ8Rzi-WddM8c2q1wrowHUfL873LUZt7z3VU4i5rLcxxaDejafdrB77j8hkhow9BHgw
Original file line number Diff line number Diff line change
@@ -1 +1,28 @@
{"keys":[{"alg":"RS256","kty":"RSA","use":"sig","x5c":["MIIDATCCAemgAwIBAgIJQSARkr+uIp3QMA0GCSqGSIb3DQEBCwUAMB4xHDAaBgNVBAMTE2ZyZW5kcy5ldS5hdXRoMC5jb20wHhcNMTgwMzI2MTEyNjU3WhcNMzExMjAzMTEyNjU3WjAeMRwwGgYDVQQDExNmcmVuZHMuZXUuYXV0aDAuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+O62mHn2qLVvdaTo9mVtRQTLOCxpBPS3HTFhSJIikDZ77jhyRJjLPGUa6udlgH36Ts8axRvcPHBq2i+Dah0tnbchNJwT7nQlpnB2CuhC+JCntffoA+q5VcJhvaPRBbI/D3QnjXlXY4n7mZKGovfQlFXlNScHI/A6YQ55MPGhui9Su0YLhNiC7wELTmR2L+rAyJJ+Tl0u1g+gLNHsTF18iJFfy6yUBLdtAgT/HeIyLblisdMrp8SMsOoWKQwUt4HA3sjF0FqyO8Rjz6RwOh6FpUTWGbUtHS/bV76QooMSfyi9tIMO5ISwpvFWPlDzIUxdqrBxJzPQUtlGkxWE41MBewIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSwaPu0o5G98jCQF1FAD68sJtRcYjAOBgNVHQ8BAf8EBAMCAoQwDQYJKoZIhvcNAQELBQADggEBAJpBr0lbEYrhTHy2PzCaOtJICzal2oK2TCJC+T1F2Bx2XFF4XxYFwwa1W6cVkoM8NvutfnYgm1qR9JSIjIKw/Ks6kpJlVb3O7RK/3lARkxI09vfoF3OWMzh31uW6k4zZX9jDYIvdEAa1l3ROImfYY1eqo5L8rUJZdMf0h368ziyUYyRDGrrGSvWo9FDgQRKZ8BjJfZKZDjp100Bml+siYi/Rl6RTdTKpQHV34VYAEdw5RHRxMm1zEm1ebAngrDKArYWaMws3cMbKnPTyX7F3To9tLnijwsjJInmbUXo/KnXPsizJaiomNCXEw48f3oPoG9tFlItRqZzatv4PeMix8p8="],"n":"-O62mHn2qLVvdaTo9mVtRQTLOCxpBPS3HTFhSJIikDZ77jhyRJjLPGUa6udlgH36Ts8axRvcPHBq2i-Dah0tnbchNJwT7nQlpnB2CuhC-JCntffoA-q5VcJhvaPRBbI_D3QnjXlXY4n7mZKGovfQlFXlNScHI_A6YQ55MPGhui9Su0YLhNiC7wELTmR2L-rAyJJ-Tl0u1g-gLNHsTF18iJFfy6yUBLdtAgT_HeIyLblisdMrp8SMsOoWKQwUt4HA3sjF0FqyO8Rjz6RwOh6FpUTWGbUtHS_bV76QooMSfyi9tIMO5ISwpvFWPlDzIUxdqrBxJzPQUtlGkxWE41MBew","e":"AQAB","kid":"MTUyRjI1QkYzQTg4NTI3OTQzRTczRTU3NUQ3NzgyODhBRDZBNTU3Mw","x5t":"MTUyRjI1QkYzQTg4NTI3OTQzRTczRTU3NUQ3NzgyODhBRDZBNTU3Mw"}]}
{
"keys": [
{
"kty":"RSA",
"alg":"RS256",
"use":"sig",
"kid":"MTUyRjI1QkYzQTg4NTI3OTQzRTczRTU3NUQ3NzgyODhBRDZBNTU3Mw",
"n":"-O62mHn2qLVvdaTo9mVtRQTLOCxpBPS3HTFhSJIikDZ77jhyRJjLPGUa6udlgH36Ts8axRvcPHBq2i-Dah0tnbchNJwT7nQlpnB2CuhC-JCntffoA-q5VcJhvaPRBbI_D3QnjXlXY4n7mZKGovfQlFXlNScHI_A6YQ55MPGhui9Su0YLhNiC7wELTmR2L-rAyJJ-Tl0u1g-gLNHsTF18iJFfy6yUBLdtAgT_HeIyLblisdMrp8SMsOoWKQwUt4HA3sjF0FqyO8Rjz6RwOh6FpUTWGbUtHS_bV76QooMSfyi9tIMO5ISwpvFWPlDzIUxdqrBxJzPQUtlGkxWE41MBew",
"e":"AQAB",
"x5c":[
"MIIDATCCAemgAwIBAgIJQSARkr+uIp3QMA0GCSqGSIb3DQEBCwUAMB4xHDAaBgNVBAMTE2ZyZW5kcy5ldS5hdXRoMC5jb20wHhcNMTgwMzI2MTEyNjU3WhcNMzExMjAzMTEyNjU3WjAeMRwwGgYDVQQDExNmcmVuZHMuZXUuYXV0aDAuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+O62mHn2qLVvdaTo9mVtRQTLOCxpBPS3HTFhSJIikDZ77jhyRJjLPGUa6udlgH36Ts8axRvcPHBq2i+Dah0tnbchNJwT7nQlpnB2CuhC+JCntffoA+q5VcJhvaPRBbI/D3QnjXlXY4n7mZKGovfQlFXlNScHI/A6YQ55MPGhui9Su0YLhNiC7wELTmR2L+rAyJJ+Tl0u1g+gLNHsTF18iJFfy6yUBLdtAgT/HeIyLblisdMrp8SMsOoWKQwUt4HA3sjF0FqyO8Rjz6RwOh6FpUTWGbUtHS/bV76QooMSfyi9tIMO5ISwpvFWPlDzIUxdqrBxJzPQUtlGkxWE41MBewIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSwaPu0o5G98jCQF1FAD68sJtRcYjAOBgNVHQ8BAf8EBAMCAoQwDQYJKoZIhvcNAQELBQADggEBAJpBr0lbEYrhTHy2PzCaOtJICzal2oK2TCJC+T1F2Bx2XFF4XxYFwwa1W6cVkoM8NvutfnYgm1qR9JSIjIKw/Ks6kpJlVb3O7RK/3lARkxI09vfoF3OWMzh31uW6k4zZX9jDYIvdEAa1l3ROImfYY1eqo5L8rUJZdMf0h368ziyUYyRDGrrGSvWo9FDgQRKZ8BjJfZKZDjp100Bml+siYi/Rl6RTdTKpQHV34VYAEdw5RHRxMm1zEm1ebAngrDKArYWaMws3cMbKnPTyX7F3To9tLnijwsjJInmbUXo/KnXPsizJaiomNCXEw48f3oPoG9tFlItRqZzatv4PeMix8p8="
],
"x5t":"MTUyRjI1QkYzQTg4NTI3OTQzRTczRTU3NUQ3NzgyODhBRDZBNTU3Mw"
},
{
"kty": "RSA",
"alg": "RS256",
"use": "sig",
"kid": "c663b9dc-e0ef-49e0-8201-e7cae8326bd2",
"n": "u1SU1LfVLPHCozMxH2Mo4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0_IzW7yWR7QkrmBL7jTKEn5u-qKhbwKfBstIs-bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyehkd3qqGElvW_VDL5AaWTg0nLVkjRo9z-40RQzuVaE8AkAFmxZzow3x-VJYKdjykkJ0iT9wCS0DRTXu269V264Vf_3jvredZiKRkgwlL9xNAwxXFg0x_XFw005UWVRIkdgcKWTjpBP2dPwVZ4WWC-9aGVd-Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbcmw",
"e": "AQAB",
"x5c": [
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u+qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyehkd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdgcKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbcmwIDAQAB"
],
"x5t": "zwCWC4hfv6CBMRjsgk9gyAphSr8"
}
]
}
12 changes: 12 additions & 0 deletions Frends.OAuth.ParseToken/Frends.OAuth.ParseToken.Tests/UnitTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace Frends.OAuth.ParseToken.Tests;
public class UnitTests
{
private static readonly string _authHeader = File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "../../../Files/AuthHeader.txt"));
private static readonly string _authHeaderWithArrayTypeClaim = File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "../../../Files/AuthHeaderWithArrayTypeClaim.txt"));
readonly JObject JwkKeys = JObject.Parse(File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "../../../Files/JwkKeys.json")));
private static Input _input = new();
private static Options _options = new();
Expand Down Expand Up @@ -54,6 +55,17 @@ public async Task ParseTokenTest_WithStaticConfiguration()
Assert.IsTrue(result.Claims.Count > 1);
}

[TestMethod]
public async Task ParseTokenTest_WithArrayTypeClaim()
{
_input.AuthHeaderOrToken = _authHeaderWithArrayTypeClaim;
_input.ConfigurationSource = ConfigurationSource.Static;
_input.StaticJwksConfiguration = JwkKeys.ToString();
var result = await OAuth.ParseToken(_input, _options, default);
Assert.IsTrue(result.Claims.Count > 1);
Assert.IsInstanceOfType(result.Claims["test_array"], typeof(Array));
}

[TestMethod]
public async Task ParseTokenTest_Invalid_Audience_Throw()
{
Expand Down
17 changes: 15 additions & 2 deletions Frends.OAuth.ParseToken/Frends.OAuth.ParseToken/ParseToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
using System.ComponentModel;
using System.IdentityModel.Tokens.Jwt;
using System.IO;
using System.Linq;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using ThirdParty.BouncyCastle.OpenSsl;
Expand Down Expand Up @@ -56,10 +58,21 @@ public static async Task<ParseResult> ParseToken([PropertyTab] Input input, [Pro
var audiences = "";
var claims = new Dictionary<string, dynamic>();

IEnumerable<Claim> claimsWithSameType;
foreach (var claim in validateToken.Claims)
{
claims.Add(claim.Type, claim.Value);
if (claim.Type == "aud") audiences = claim.Value;
if (claims.ContainsKey(claim.Type)) continue;

claimsWithSameType = validateToken.Claims.Where(c => c.Type == claim.Type);
if (claimsWithSameType.Count() > 1)
{
claims.Add(claim.Type, claimsWithSameType.Select(c => c.Value).ToArray());
}
else
{
claims.Add(claim.Type, claim.Value);
if (claim.Type == "aud") audiences = claim.Value;
}
}

return new ParseResult(
Expand Down
44 changes: 44 additions & 0 deletions Frends.OAuth.ParseToken/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,47 @@ Rebuild the project
Run tests

`dotnet test`


### Keys for testing JWTs

AuthHeaderWithArrayTypeClaim.txt

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo
4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u
+qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyeh
kd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ
0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdg
cKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbc
mwIDAQAB
-----END PUBLIC KEY-----

-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7VJTUt9Us8cKj
MzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvu
NMoSfm76oqFvAp8Gy0iz5sxjZmSnXyCdPEovGhLa0VzMaQ8s+CLOyS56YyCFGeJZ
qgtzJ6GR3eqoYSW9b9UMvkBpZODSctWSNGj3P7jRFDO5VoTwCQAWbFnOjDfH5Ulg
p2PKSQnSJP3AJLQNFNe7br1XbrhV//eO+t51mIpGSDCUv3E0DDFcWDTH9cXDTTlR
ZVEiR2BwpZOOkE/Z0/BVnhZYL71oZV34bKfWjQIt6V/isSMahdsAASACp4ZTGtwi
VuNd9tybAgMBAAECggEBAKTmjaS6tkK8BlPXClTQ2vpz/N6uxDeS35mXpqasqskV
laAidgg/sWqpjXDbXr93otIMLlWsM+X0CqMDgSXKejLS2jx4GDjI1ZTXg++0AMJ8
sJ74pWzVDOfmCEQ/7wXs3+cbnXhKriO8Z036q92Qc1+N87SI38nkGa0ABH9CN83H
mQqt4fB7UdHzuIRe/me2PGhIq5ZBzj6h3BpoPGzEP+x3l9YmK8t/1cN0pqI+dQwY
dgfGjackLu/2qH80MCF7IyQaseZUOJyKrCLtSD/Iixv/hzDEUPfOCjFDgTpzf3cw
ta8+oE4wHCo1iI1/4TlPkwmXx4qSXtmw4aQPz7IDQvECgYEA8KNThCO2gsC2I9PQ
DM/8Cw0O983WCDY+oi+7JPiNAJwv5DYBqEZB1QYdj06YD16XlC/HAZMsMku1na2T
N0driwenQQWzoev3g2S7gRDoS/FCJSI3jJ+kjgtaA7Qmzlgk1TxODN+G1H91HW7t
0l7VnL27IWyYo2qRRK3jzxqUiPUCgYEAx0oQs2reBQGMVZnApD1jeq7n4MvNLcPv
t8b/eU9iUv6Y4Mj0Suo/AU8lYZXm8ubbqAlwz2VSVunD2tOplHyMUrtCtObAfVDU
AhCndKaA9gApgfb3xw1IKbuQ1u4IF1FJl3VtumfQn//LiH1B3rXhcdyo3/vIttEk
48RakUKClU8CgYEAzV7W3COOlDDcQd935DdtKBFRAPRPAlspQUnzMi5eSHMD/ISL
DY5IiQHbIH83D4bvXq0X7qQoSBSNP7Dvv3HYuqMhf0DaegrlBuJllFVVq9qPVRnK
xt1Il2HgxOBvbhOT+9in1BzA+YJ99UzC85O0Qz06A+CmtHEy4aZ2kj5hHjECgYEA
mNS4+A8Fkss8Js1RieK2LniBxMgmYml3pfVLKGnzmng7H2+cwPLhPIzIuwytXywh
2bzbsYEfYx3EoEVgMEpPhoarQnYPukrJO4gwE2o5Te6T5mJSZGlQJQj9q4ZB2Dfz
et6INsK0oG8XVGXSpQvQh3RUYekCZQkBBFcpqWpbIEsCgYAnM3DQf3FJoSnXaMhr
VBIovic5l0xFkEHskAjFTevO86Fsz1C2aSeRKSqGFoOQ0tmJzBEs1R6KqnHInicD
TQrKhArgLXX4v3CddjfTRJkFWDbE/CkvKZNOrcf1nhaGCPspRJj2KUkj1Fhl9Cnc
dn/RsYEONbwQSjIfMPkvxF+8HQ==
-----END PRIVATE KEY-----
Loading