/// <summary> /// Parses the UTF-8 <paramref name="buffer"/> as JSON and returns a <see cref="JwtHeader"/>. /// </summary> /// <param name="buffer"></param> /// <param name="policy"></param> public static JwtHeader ParseHeader(ReadOnlySpan <byte> buffer, TokenValidationPolicy policy) { Utf8JsonReader reader = new Utf8JsonReader(buffer, isFinalBlock: true, state: default); if (!reader.Read() || reader.TokenType != JsonTokenType.StartObject) { ThrowHelper.ThrowFormatException_MalformedJson(); } var header = new JwtHeader(); while (reader.Read()) { if (!(reader.TokenType is JsonTokenType.PropertyName)) { break; } var name = reader.ValueSpan /* reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan */; reader.Read(); var type = reader.TokenType; if (name.Length == 3) { if (reader.TokenType == JsonTokenType.String) { var refName = IntegerMarshal.ReadUInt24(name); switch (refName) { case Alg: var alg = reader.ValueSpan /* reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan */; if (SignatureAlgorithm.TryParse(alg, out var signatureAlgorithm)) { header.SignatureAlgorithm = signatureAlgorithm; } else if (KeyManagementAlgorithm.TryParse(alg, out var keyManagementAlgorithm)) { header.KeyManagementAlgorithm = keyManagementAlgorithm; } else if (SignatureAlgorithm.TryParseSlow(ref reader, out signatureAlgorithm)) { header.SignatureAlgorithm = signatureAlgorithm; } else if (KeyManagementAlgorithm.TryParseSlow(ref reader, out keyManagementAlgorithm)) { header.KeyManagementAlgorithm = keyManagementAlgorithm; } else { header.SignatureAlgorithm = SignatureAlgorithm.Create(reader.GetString()); } continue; case Enc: var enc = reader.ValueSpan /* reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan */; if (EncryptionAlgorithm.TryParse(enc, out var encryptionAlgorithm)) { header.EncryptionAlgorithm = encryptionAlgorithm; } else if (EncryptionAlgorithm.TryParseSlow(ref reader, out encryptionAlgorithm)) { header.EncryptionAlgorithm = encryptionAlgorithm; } else { header.EncryptionAlgorithm = EncryptionAlgorithm.Create(reader.GetString()); } continue; case Zip: var zip = reader.ValueSpan /* reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan */; if (CompressionAlgorithm.TryParse(zip, out var compressionAlgorithm)) { header.CompressionAlgorithm = compressionAlgorithm; } else if (CompressionAlgorithm.TryParseSlow(ref reader, out compressionAlgorithm)) { header.CompressionAlgorithm = compressionAlgorithm; } else { header.CompressionAlgorithm = CompressionAlgorithm.Create(reader.GetString()); } continue; case Cty: header.Cty = reader.GetString(); continue; case Typ: header.Typ = reader.GetString(); continue; case Kid: header.Kid = reader.GetString(); continue; } } } else if (name.Length == 4) { if (reader.TokenType == JsonTokenType.StartArray && IntegerMarshal.ReadUInt32(name) == Crit) { var handlers = policy.CriticalHandlers; if (handlers.Count != 0) { var criticalHeaderHandlers = new List <KeyValuePair <string, ICriticalHeaderHandler> >(handlers.Count); var criticals = new List <JwtValue>(); while (reader.Read() && reader.TokenType == JsonTokenType.String) { string criticalHeader = reader.GetString(); criticals.Add(new JwtValue(criticalHeader)); if (handlers.TryGetValue(criticalHeader, out var handler)) { criticalHeaderHandlers.Add(new KeyValuePair <string, ICriticalHeaderHandler>(criticalHeader, handler)); } else { criticalHeaderHandlers.Add(new KeyValuePair <string, ICriticalHeaderHandler>(criticalHeader, null !)); } } header.CriticalHeaderHandlers = criticalHeaderHandlers; if (reader.TokenType != JsonTokenType.EndArray) { ThrowHelper.ThrowFormatException_MalformedJson("The 'crit' header parameter must be an array of string."); } header.Inner.Add(name, new JwtArray(criticals)); } else { var criticals = new List <JwtValue>(); while (reader.Read() && reader.TokenType == JsonTokenType.String) { string criticalHeader = reader.GetString(); criticals.Add(new JwtValue(criticalHeader)); } if (reader.TokenType != JsonTokenType.EndArray) { ThrowHelper.ThrowFormatException_MalformedJson("The 'crit' header parameter must be an array of string."); } header.Inner.Add(name, new JwtArray(criticals)); } continue; } } switch (type) { case JsonTokenType.StartObject: header.Inner.Add(name, JsonParser.ReadJsonObject(ref reader)); break; case JsonTokenType.StartArray: header.Inner.Add(name, JsonParser.ReadJsonArray(ref reader)); break; case JsonTokenType.String: header.Inner.Add(name, reader.GetString()); break; case JsonTokenType.True: header.Inner.Add(name, true); break; case JsonTokenType.False: header.Inner.Add(name, false); break; case JsonTokenType.Null: header.Inner.Add(name); break; case JsonTokenType.Number: if (reader.TryGetInt64(out long longValue)) { header.Inner.Add(name, longValue); } else { header.Inner.Add(name, reader.GetDouble()); } break; default: ThrowHelper.ThrowFormatException_MalformedJson(); break; } } if (!(reader.TokenType is JsonTokenType.EndObject)) { ThrowHelper.ThrowFormatException_MalformedJson(); } return(header); }
private static void ReadArrayProperty(ref Utf8JsonReader reader, OpenIdConnectConfiguration config, string propertyName) { switch (propertyName) { case OpenIdProviderMetadataNames.AcrValuesSupported: config.AcrValuesSupported = GetStringArray(ref reader); break; case OpenIdProviderMetadataNames.ClaimsSupported: config.ClaimsSupported = GetStringArray(ref reader); break; case OpenIdProviderMetadataNames.ClaimsLocalesSupported: config.ClaimsLocalesSupported = GetStringArray(ref reader); break; case OpenIdProviderMetadataNames.ClaimTypesSupported: config.ClaimTypesSupported = GetStringArray(ref reader); break; case OpenIdProviderMetadataNames.DisplayValuesSupported: config.DisplayValuesSupported = GetStringArray(ref reader); break; case OpenIdProviderMetadataNames.GrantTypesSupported: config.GrantTypesSupported = GetStringArray(ref reader); break; case OpenIdProviderMetadataNames.IdTokenEncryptionAlgValuesSupported: config.IdTokenEncryptionAlgValuesSupported = GetStringArray(ref reader); break; case OpenIdProviderMetadataNames.IdTokenEncryptionEncValuesSupported: config.IdTokenEncryptionEncValuesSupported = GetStringArray(ref reader); break; case OpenIdProviderMetadataNames.IdTokenSigningAlgValuesSupported: config.IdTokenSigningAlgValuesSupported = GetStringArray(ref reader); break; case OpenIdProviderMetadataNames.RequestObjectEncryptionAlgValuesSupported: config.RequestObjectEncryptionAlgValuesSupported = GetStringArray(ref reader); break; case OpenIdProviderMetadataNames.RequestObjectEncryptionEncValuesSupported: config.RequestObjectEncryptionEncValuesSupported = GetStringArray(ref reader); break; case OpenIdProviderMetadataNames.RequestObjectSigningAlgValuesSupported: config.RequestObjectSigningAlgValuesSupported = GetStringArray(ref reader); break; case OpenIdProviderMetadataNames.ResponseModesSupported: config.ResponseModesSupported = GetStringArray(ref reader); break; case OpenIdProviderMetadataNames.ResponseTypesSupported: config.ResponseTypesSupported = GetStringArray(ref reader); break; case OpenIdProviderMetadataNames.ScopesSupported: config.ScopesSupported = GetStringArray(ref reader); break; case OpenIdProviderMetadataNames.SubjectTypesSupported: config.SubjectTypesSupported = GetStringArray(ref reader); break; case OpenIdProviderMetadataNames.TokenEndpointAuthMethodsSupported: config.TokenEndpointAuthMethodsSupported = GetStringArray(ref reader); break; case OpenIdProviderMetadataNames.TokenEndpointAuthSigningAlgValuesSupported: config.TokenEndpointAuthSigningAlgValuesSupported = GetStringArray(ref reader); break; case OpenIdProviderMetadataNames.UILocalesSupported: config.UILocalesSupported = GetStringArray(ref reader); break; case OpenIdProviderMetadataNames.UserInfoEncryptionAlgValuesSupported: config.UserInfoEncryptionAlgValuesSupported = GetStringArray(ref reader); break; case OpenIdProviderMetadataNames.UserInfoEncryptionEncValuesSupported: config.UserInfoEncryptionEncValuesSupported = GetStringArray(ref reader); break; case OpenIdProviderMetadataNames.UserInfoSigningAlgValuesSupported: config.UserInfoSigningAlgValuesSupported = GetStringArray(ref reader); break; case OpenIdProviderMetadataNames.RevocationEndpointAuthMethodsSupported: config.RevocationEndpointAuthMethodsSupported = GetStringArray(ref reader); break; case OpenIdProviderMetadataNames.RevocationEndpointAuthSigningAlgValuesSupported: config.RevocationEndpointAuthSigningAlgValuesSupported = GetStringArray(ref reader); break; case OpenIdProviderMetadataNames.IntrospectionEndpointAuthMethodsSupported: config.IntrospectionEndpointAuthMethodsSupported = GetStringArray(ref reader); break; case OpenIdProviderMetadataNames.IntrospectionEndpointAuthSigningAlgValuesSupported: config.IntrospectionEndpointAuthSigningAlgValuesSupported = GetStringArray(ref reader); break; case OpenIdProviderMetadataNames.CodeChallengeMethodsSupported: config.CodeChallengeMethodsSupported = GetStringArray(ref reader); break; default: config.AdditionalData.Add(new JwtProperty(propertyName, JsonParser.ReadJsonArray(ref reader))); break; } }
/// <summary> /// Parses the UTF-8 <paramref name="buffer"/> as JSON and returns a <see cref="JwtPayload"/>. /// </summary> /// <param name="buffer"></param> /// <param name="policy"></param> public static JwtPayload ParsePayload(ReadOnlySpan <byte> buffer, TokenValidationPolicy policy) { Utf8JsonReader reader = new Utf8JsonReader(buffer, isFinalBlock: true, state: default); if (!reader.Read() || reader.TokenType != JsonTokenType.StartObject) { ThrowHelper.ThrowFormatException_MalformedJson(); } var payload = new JwtPayload(); byte control = policy.ValidationControl; while (reader.Read() && reader.TokenType is JsonTokenType.PropertyName) { var name = reader.ValueSpan /* reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan */; reader.Read(); var type = reader.TokenType; if (name.Length == 3) { uint nameValue = IntegerMarshal.ReadUInt24(name); switch (nameValue) { case Aud: if (type == JsonTokenType.String) { if (policy.RequireAudience) { var audiencesBinary = policy.RequiredAudiencesBinary; var audiences = policy.RequiredAudiences; for (int i = 0; i < audiencesBinary.Length; i++) { if (reader.ValueTextEquals(audiencesBinary[i])) { payload.Aud = new[] { audiences[i] }; control &= unchecked ((byte)~TokenValidationPolicy.AudienceFlag); break; } } control &= unchecked ((byte)~JwtPayload.MissingAudienceFlag); } else { payload.Aud = new[] { reader.GetString() }; } } else if (type == JsonTokenType.StartArray) { if (policy.RequireAudience) { var audiences = new List <string>(); while (reader.Read() && reader.TokenType == JsonTokenType.String) { var requiredAudiences = policy.RequiredAudiencesBinary; for (int i = 0; i < requiredAudiences.Length; i++) { if (reader.ValueTextEquals(requiredAudiences[i])) { control &= unchecked ((byte)~TokenValidationPolicy.AudienceFlag); break; } } audiences.Add(reader.GetString()); control &= unchecked ((byte)~JwtPayload.MissingAudienceFlag); } if (reader.TokenType != JsonTokenType.EndArray) { ThrowHelper.ThrowFormatException_MalformedJson("The 'aud' claim must be an array of string or a string."); } payload.Aud = audiences.ToArray(); } else { payload.Aud = JsonParser.ReadStringArray(ref reader); } } else { ThrowHelper.ThrowFormatException_MalformedJson("The 'aud' claim must be an array of string or a string."); } continue; case Iss: if (policy.RequireIssuer) { if (reader.ValueTextEquals(policy.RequiredIssuerBinary)) { payload.Iss = policy.RequiredIssuer; control &= unchecked ((byte)~TokenValidationPolicy.IssuerFlag); } else { control &= unchecked ((byte)~JwtPayload.MissingIssuerFlag); } } else { payload.Iss = reader.GetString(); } continue; case Exp: if (!reader.TryGetInt64(out long longValue)) { ThrowHelper.ThrowFormatException_MalformedJson("The claim 'exp' must be an integral number."); } if (policy.RequireExpirationTime) { control &= unchecked ((byte)~JwtPayload.MissingExpirationFlag); } if (longValue >= EpochTime.UtcNow - policy.ClockSkew) { control &= unchecked ((byte)~JwtPayload.ExpiredFlag); } payload.Exp = longValue; continue; case Iat: if (!reader.TryGetInt64(out longValue)) { ThrowHelper.ThrowFormatException_MalformedJson("The claim 'iat' must be an integral number."); } payload.Iat = longValue; continue; case Nbf: if (!reader.TryGetInt64(out longValue)) { ThrowHelper.ThrowFormatException_MalformedJson("The claim 'nbf' must be an integral number."); } // the 'nbf' claim is not common. The 2nd call to EpochTime.UtcNow should be rare. if (longValue <= EpochTime.UtcNow + policy.ClockSkew) { control &= unchecked ((byte)~JwtPayload.NotYetFlag); } payload.Nbf = longValue; continue; case Jti: payload.Jti = reader.GetString(); continue; case Sub: payload.Sub = reader.GetString(); continue; } } switch (type) { case JsonTokenType.StartObject: payload.Inner.Add(name, JsonParser.ReadJsonObject(ref reader)); break; case JsonTokenType.StartArray: payload.Inner.Add(name, JsonParser.ReadJsonArray(ref reader)); break; case JsonTokenType.String: payload.Inner.Add(name, reader.GetString()); break; case JsonTokenType.True: payload.Inner.Add(name, true); break; case JsonTokenType.False: payload.Inner.Add(name, false); break; case JsonTokenType.Null: payload.Inner.Add(name); break; case JsonTokenType.Number: long longValue; if (reader.TryGetInt64(out longValue)) { payload.Inner.Add(name, longValue); } else { payload.Inner.Add(name, reader.GetDouble()); } break; default: ThrowHelper.ThrowFormatException_MalformedJson(); break; } } if (!(reader.TokenType is JsonTokenType.EndObject)) { ThrowHelper.ThrowFormatException_MalformedJson(); } payload.ValidationControl = control; return(payload); }