/// <summary>Publish an event that notify a <see cref="Jwks"/> has been refreshed.</summary> public static void PublishJwksRefreshed(Jwks oldJwks, Jwks newJwks) { if (OnJwksRefreshed != null) { var added = new Jwks(newJwks.Issuer); var removed = new Jwks(newJwks.Issuer); for (int i = 0; i < oldJwks.Count; i++) { var key = oldJwks._keys[i]; if (!newJwks.Contains(key)) { removed._keys.Add(key); } } for (int i = 0; i < newJwks.Count; i++) { var key = newJwks._keys[i]; if (!oldJwks.Contains(key)) { added._keys.Add(key); } } if (added.Count != 0 || removed.Count != 0) { OnJwksRefreshed(added, removed); } } }
/// <inheritdoc/> protected override Jwks GetKeysFromSource() { using var retriever = _documentRetrieverFactory(); var value = retriever.GetDocument(_jwksAddress, CancellationToken.None); return(Jwks.FromJson(Issuer, value)); }
/// <summary>Returns a new instance of <see cref="Jwks"/>.</summary> /// <param name="issuer">The issuer of the keys.</param> /// <param name="json">a string that contains JSON Web Key parameters in JSON format.</param> public static Jwks FromJson(string issuer, ReadOnlySpan <byte> json) { // a JWKS is : // { // "keys": [ // { jwk1 }, // { jwk2 }, // ... // ] // } var jwks = new Jwks(issuer); var reader = new Utf8JsonReader(json, true, default); if (reader.Read() && reader.TokenType is JsonTokenType.StartObject && reader.Read()) { while (reader.TokenType is JsonTokenType.PropertyName) { var propertyName = reader.ValueSpan; if (propertyName.Length == 4) { ref byte propertyNameRef = ref MemoryMarshal.GetReference(propertyName); if (IntegerMarshal.ReadUInt32(ref propertyNameRef) == keys /* keys */) { if (reader.Read() && reader.TokenType is JsonTokenType.StartArray) { while (reader.Read() && reader.TokenType is JsonTokenType.StartObject) { Jwk jwk = Jwk.FromJsonReader(ref reader); jwks.Add(jwk); } if (!(reader.TokenType is JsonTokenType.EndArray) || !reader.Read()) { ThrowHelper.ThrowInvalidOperationException_MalformedJwks(); } } continue; } } reader.Read(); if (reader.TokenType >= JsonTokenType.String && reader.TokenType <= JsonTokenType.Null) { reader.Read(); } else if (reader.TokenType == JsonTokenType.StartObject) { JsonParser.ConsumeJsonObject(ref reader); } else if (reader.TokenType == JsonTokenType.StartArray) { JsonParser.ConsumeJsonArray(ref reader); } else { ThrowHelper.ThrowInvalidOperationException_MalformedJwks(); } } }
/// <summary>Configure the signature behavior for a specific <paramref name="issuer"/>.</summary> public TokenValidationPolicyBuilder RequireSignature(string issuer, Jwks keys, SignatureAlgorithm defaultAlgorithm) { if (keys is null) { throw new ArgumentNullException(nameof(keys)); } return(RequireSignatureByDefault(issuer, new StaticKeyProvider(keys), defaultAlgorithm)); }
/// <summary> /// Initializes a new instance of <see cref="StaticKeyProvider"/>. /// </summary> /// <param name="jwks"></param> public StaticKeyProvider(Jwks jwks) { if (jwks is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.jwks); } _jwks = jwks; }
/// <inheritsdoc /> public Jwk[] GetKeys(JwtHeaderDocument header) { if (_disposed) { ThrowHelper.ThrowObjectDisposedException(typeof(CachedKeyProvider)); } var kid = header.Kid; long now = EpochTime.UtcNow; bool forceSync = false; if (_syncAfter > now) { var keys = _currentJwks.GetKeys(kid); if (keys.Length != 0) { return(keys); } // force the refresh only when the latest forced refresh is not too old if (_forcedSyncAfter <= now) { _forcedSyncAfter = now + MinimumRefreshInterval; forceSync = true; } } if (forceSync || _syncAfter <= now) { Jwks previous = _currentJwks; Jwks refreshedJwks; _refreshLock.Wait(); try { if (forceSync || _syncAfter <= now) { refreshedJwks = GetKeysFromSource(); _currentJwks = refreshedJwks; _syncAfter = now + AutomaticRefreshInterval; Jwks.PublishJwksRefreshed(previous, refreshedJwks); } } catch { _syncAfter = now + Math.Min(AutomaticRefreshInterval, MinimumRefreshInterval); throw; } finally { _refreshLock.Release(); } } return(_currentJwks.GetKeys(kid)); }
/// <inheritsdoc /> private Jwk[] GetKeys(JwtHeaderDocument header, string metadataAddress) { if (_disposed) { ThrowHelper.ThrowObjectDisposedException(typeof(JwksHttpKeyProvider)); } var kid = header.Kid; long now = EpochTime.UtcNow; if (_currentJwks != null && _syncAfter > now) { return(_currentJwks.GetKeys(kid)); } if (_syncAfter <= now) { _refreshLock.Wait(); try { var value = _documentRetriever.GetDocument(metadataAddress, CancellationToken.None); var refreshedJwks = Jwks.FromJson(Issuer, value); Jwks.PublishJwksRefreshed(refreshedJwks); _currentJwks = refreshedJwks; _syncAfter = now + AutomaticRefreshInterval; } catch { _syncAfter = now + (AutomaticRefreshInterval < RefreshInterval ? AutomaticRefreshInterval : RefreshInterval); throw; } finally { _refreshLock.Release(); } } if (_currentJwks != null) { return(_currentJwks.GetKeys(kid)); } ThrowHelper.ThrowInvalidOperationException_UnableToObtainKeysException(metadataAddress); return(Array.Empty <Jwk>()); }
/// <inheritsdoc /> protected Jwk[] GetKeys(JwtHeader header, string metadataAddress) { if (_disposed) { ThrowHelper.ThrowObjectDisposedException(GetType()); } var kid = header.Kid; var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); if (_currentKeys != null && _syncAfter > now) { return(_currentKeys.GetKeys(kid)); } if (_syncAfter <= now) { _refreshLock.Wait(); try { var value = _documentRetriever.GetDocument(metadataAddress, CancellationToken.None); _currentKeys = Jwks.FromJson(value); _syncAfter = now + AutomaticRefreshInterval; } catch { _syncAfter = now + (AutomaticRefreshInterval < RefreshInterval ? AutomaticRefreshInterval : RefreshInterval); throw; } finally { _refreshLock.Release(); } } if (_currentKeys != null) { return(_currentKeys.GetKeys(kid)); } ThrowHelper.ThrowInvalidOperationException_UnableToObtainKeysException(metadataAddress); return(Array.Empty <Jwk>()); }
/// <summary> /// Returns a new instance of <see cref="Jwks"/>. /// </summary> /// <param name="json">a string that contains JSON Web Key parameters in JSON format.</param> /// <returns><see cref="Jwks"/></returns> public static Jwks FromJson(ReadOnlySpan <byte> json) { // a JWKS is : // { // "keys": [ // { jwk1 }, // { jwk2 }, // ... // ] // } var jwks = new Jwks(); var reader = new Utf8JsonReader(json, true, default); if (reader.Read() && reader.TokenType is JsonTokenType.StartObject && reader.Read() && reader.TokenType is JsonTokenType.PropertyName) { var propertyName = reader.ValueSpan /* reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan */; if (propertyName.Length == 4) { ref byte propertyNameRef = ref MemoryMarshal.GetReference(propertyName); if (IntegerMarshal.ReadUInt32(ref propertyNameRef) == keys /* keys */) { if (reader.Read() && reader.TokenType is JsonTokenType.StartArray) { while (reader.Read() && reader.TokenType is JsonTokenType.StartObject) { Jwk jwk = Jwk.FromJsonReader(ref reader); jwks.Add(jwk); } if (!(reader.TokenType is JsonTokenType.EndArray) || !reader.Read()) { ThrowHelper.ThrowInvalidOperationException_MalformedJwks(); } } } } }
public JwtReader(Jwks encryptionKeys) => throw new NotImplementedException();
public TokenValidationPolicyBuilder RequireSignature(Jwks keySet, SignatureAlgorithm?algorithm) => throw new NotSupportedException();
/// <summary>Publish an event that notify a <see cref="Jwks"/> has been refreshed.</summary> public static void PublishJwksRefreshed(Jwks keys) => OnJwksRefreshed?.Invoke(keys);
/// <inheritsdoc /> protected override Jwks DeserializeKeySet(string value) { return(Jwks.FromJson(value)); }
/// <summary>Initializes a new instance of <see cref="CachedKeyProvider"/> class.</summary> protected CachedKeyProvider(long minimumRefreshInterval = DefaultMinimumRefreshInterval, long automaticRefreshInterval = DefaultAutomaticRefreshInterval) { _currentJwks = EmptyJwks; MinimumRefreshInterval = minimumRefreshInterval; AutomaticRefreshInterval = automaticRefreshInterval; }
/// <summary>Defines the <see cref="Jwks"/> used to decrypt the tokens.</summary> public TokenValidationPolicyBuilder WithDecryptionKeys(Jwks decryptionKeys) => WithDecryptionKeys(new StaticKeyProvider(decryptionKeys));
/// <summary> /// Defines the default signature validation when there is no issuer configuration. /// Use the method <see cref="RequireSignatureByDefault(string, IKeyProvider, SignatureAlgorithm?)"/> for linking the issuer with the signature. /// </summary> public TokenValidationPolicyBuilder RequireSignatureByDefault(Jwks keySet, SignatureAlgorithm?algorithm) => RequireSignatureByDefault(new StaticKeyProvider(keySet), algorithm);
/// <summary> /// Defines the default signature validation when there is no issuer configuration. /// Use the method <see cref="RequireSignatureByDefault(string, IKeyProvider, SignatureAlgorithm?)"/> for linking the issuer with the signature. /// </summary> public TokenValidationPolicyBuilder RequireSignatureByDefault(Jwks keySet) => RequireSignatureByDefault(keySet, null);