Beispiel #1
0
        public async Task <Option <Client> > Build(Client newClient, bool updateId = true, CancellationToken cancellationToken = default)
        {
            var result = ValidateNotMandatoryUri(newClient.ClientUri, "client_uri");

            if (result is Option.Error e)
            {
                _logger.LogError(e.Details.Detail);
                return(new Option <Client> .Error(e.Details, e.State));
            }
            result = ValidateNotMandatoryUri(newClient.TosUri, "tos_uri");
            if (result is Option.Error e2)
            {
                _logger.LogError(e2.Details.Detail);
                return(new Option <Client> .Error(e2.Details, e2.State));
            }
            result = ValidateNotMandatoryUri(newClient.SectorIdentifierUri, "sector_identifier_uri", true);
            if (result is Option.Error e3)
            {
                _logger.LogError(e3.Details.Detail);
                return(new Option <Client> .Error(e3.Details, e3.State));
            }
            // Based on the RFC : http://openid.net/specs/openid-connect-registration-1_0.html#SectorIdentifierValidation validate the sector_identifier_uri
            if (newClient.SectorIdentifierUri != null)
            {
                var sectorIdentifierUrisOption =
                    await GetSectorIdentifierUris(newClient.SectorIdentifierUri, cancellationToken).ConfigureAwait(false);

                if (sectorIdentifierUrisOption is Option <IReadOnlyCollection <Uri> > .Error error)
                {
                    return(new Option <Client> .Error(error.Details, error.State));
                }

                var sectorIdentifierUris = ((Option <IReadOnlyCollection <Uri> > .Result)sectorIdentifierUrisOption).Item;
                if (sectorIdentifierUris.Any(
                        sectorIdentifierUri => !newClient.RedirectionUrls.Contains(sectorIdentifierUri)))
                {
                    _logger.LogError(Strings.OneOrMoreSectorIdentifierUriIsNotARedirectUri);
                    return(new Option <Client> .Error(new ErrorDetails
                    {
                        Title = ErrorCodes.InvalidClientMetaData,
                        Detail = Strings.OneOrMoreSectorIdentifierUriIsNotARedirectUri,
                        Status = HttpStatusCode.BadRequest
                    }));
                }
            }

            if (!string.IsNullOrWhiteSpace(newClient.IdTokenEncryptedResponseEnc) && string.IsNullOrWhiteSpace(newClient.IdTokenEncryptedResponseAlg))
            {
                _logger.LogError(Strings.TheParameterIsTokenEncryptedResponseAlgMustBeSpecified);
                return(new Option <Client> .Error(new ErrorDetails
                {
                    Title = ErrorCodes.InvalidClientMetaData,
                    Detail = Strings.TheParameterIsTokenEncryptedResponseAlgMustBeSpecified,
                    Status = HttpStatusCode.BadRequest
                }));
            }

            if (!string.IsNullOrWhiteSpace(newClient.UserInfoEncryptedResponseEnc) && string.IsNullOrWhiteSpace(newClient.UserInfoEncryptedResponseAlg))
            {
                _logger.LogError(Strings.TheParameterUserInfoEncryptedResponseAlgMustBeSpecified);
                return(new Option <Client> .Error(new ErrorDetails
                {
                    Title = ErrorCodes.InvalidClientMetaData,
                    Detail = Strings.TheParameterUserInfoEncryptedResponseAlgMustBeSpecified,
                    Status = HttpStatusCode.BadRequest
                }));
            }

            if (!string.IsNullOrWhiteSpace(newClient.RequestObjectEncryptionEnc) && string.IsNullOrWhiteSpace(newClient.RequestObjectEncryptionAlg))
            {
                _logger.LogError(Strings.TheParameterRequestObjectEncryptionAlgMustBeSpecified);
                return(new Option <Client> .Error(new ErrorDetails
                {
                    Title = ErrorCodes.InvalidClientMetaData,
                    Detail = Strings.TheParameterRequestObjectEncryptionAlgMustBeSpecified,
                    Status = HttpStatusCode.BadRequest
                }));
            }

            if (newClient.RedirectionUrls.Length == 0)
            {
                var message = string.Format(Strings.MissingParameter, "redirect_uris");
                _logger.LogError(message);
                return(new Option <Client> .Error(new ErrorDetails
                {
                    Title = ErrorCodes.InvalidRedirectUri,
                    Detail = message,
                    Status = HttpStatusCode.BadRequest
                }));
            }

            result = ValidateNotMandatoryUri(newClient.InitiateLoginUri, "initiate_login_uri", true);
            if (result is Option.Error e4)
            {
                return(new Option <Client> .Error(e4.Details, e4.State));
            }
            if (newClient.RequestUris.Any(requestUri => !requestUri.IsAbsoluteUri))
            {
                var message = Strings.OneOfTheRequestUriIsNotValid;
                _logger.LogError(message);
                return(new Option <Client> .Error(new ErrorDetails
                {
                    Title = ErrorCodes.InvalidClientMetaData,
                    Detail = message,
                    Status = HttpStatusCode.BadRequest
                }));
            }

            var client = new Client
            {
                ClientId = updateId ? Id.Create() : newClient.ClientId
            };

            client.ClientName = string.IsNullOrWhiteSpace(newClient.ClientName)
                ? "Unnamed_" + client.ClientId
                : newClient.ClientName;

            client.TokenLifetime    = newClient.TokenLifetime;
            client.ApplicationType  = newClient.ApplicationType;
            client.ClientUri        = newClient.ClientUri;
            client.Contacts         = newClient.Contacts;
            client.DefaultAcrValues = newClient.DefaultAcrValues;

            // If omitted then the default value is authorization code response type
            client.ResponseTypes               = newClient.ResponseTypes.Length == 0 ? new[] { ResponseTypeNames.Code } : newClient.ResponseTypes;
            client.SectorIdentifierUri         = newClient.SectorIdentifierUri;
            client.TokenEndPointAuthMethod     = newClient.TokenEndPointAuthMethod;
            client.TokenEndPointAuthSigningAlg = newClient.TokenEndPointAuthSigningAlg;
            client.TosUri = newClient.TosUri;
            client.UserInfoEncryptedResponseAlg = newClient.UserInfoEncryptedResponseAlg;
            client.UserInfoEncryptedResponseEnc = newClient.UserInfoEncryptedResponseEnc;

            client.Secrets = newClient.Secrets.Length switch
            {
                0 when client.TokenEndPointAuthMethod != TokenEndPointAuthenticationMethods.PrivateKeyJwt => new[]
                {
                    new ClientSecret {
                        Type = ClientSecretTypes.SharedSecret, Value = Id.Create()
                    }
                },
                > 0 => newClient.Secrets.Select(
                    secret => secret.Type == ClientSecretTypes.SharedSecret
                            ? new ClientSecret {
                    Type = ClientSecretTypes.SharedSecret, Value = Id.Create()
                }
                            : secret)
                .ToArray(),
                _ => client.Secrets
            };

            // If omitted then the default value is authorization code grant type
            client.GrantTypes = newClient.GrantTypes.Length == 0
                ? new[] { GrantTypes.AuthorizationCode }
                : newClient.GrantTypes;

            client.IdTokenEncryptedResponseAlg = !string.IsNullOrWhiteSpace(newClient.IdTokenEncryptedResponseAlg)
                ? newClient.IdTokenEncryptedResponseAlg
                : string.Empty;

            if (!string.IsNullOrWhiteSpace(client.IdTokenEncryptedResponseAlg))
            {
                client.IdTokenEncryptedResponseEnc = !string.IsNullOrWhiteSpace(newClient.IdTokenEncryptedResponseEnc)
                    ? newClient.IdTokenEncryptedResponseEnc
                    : SecurityAlgorithms.Aes128CbcHmacSha256;
            }
            else if (!string.IsNullOrWhiteSpace(newClient.IdTokenEncryptedResponseEnc))
            {
                var message = Strings.TheParameterIsTokenEncryptedResponseAlgMustBeSpecified;
                _logger.LogError(message);
                return(new Option <Client> .Error(new ErrorDetails
                {
                    Title = ErrorCodes.InvalidClientMetaData,
                    Detail = message,
                    Status = HttpStatusCode.BadRequest
                }));
            }

            client.IdTokenSignedResponseAlg = !string.IsNullOrWhiteSpace(newClient.IdTokenSignedResponseAlg)
                ? newClient.IdTokenSignedResponseAlg
                : SecurityAlgorithms.RsaSha256;

            client.InitiateLoginUri = newClient.InitiateLoginUri;

            client.JsonWebKeys            = newClient.JsonWebKeys;
            client.PolicyUri              = newClient.PolicyUri;
            client.PostLogoutRedirectUris = newClient.PostLogoutRedirectUris;

            //newClient.AllowedScopes ??= Array.Empty<string>();

            var scopes = await _scopeRepository.SearchByNames(CancellationToken.None, newClient.AllowedScopes)
                         .ConfigureAwait(false);

            if (scopes.Length != newClient.AllowedScopes.Length)
            {
                var enumerable = newClient.AllowedScopes.Except(scopes.Select(x => x.Name));
                var message    = $"Unknown scopes: {string.Join(",", enumerable)}";
                _logger.LogError(message);
                return(new Option <Client> .Error(new ErrorDetails
                {
                    Title = ErrorCodes.InvalidScope,
                    Detail = message,
                    Status = HttpStatusCode.BadRequest
                }));
            }

            client.AllowedScopes = newClient.AllowedScopes.ToArray();

            // Check the newClients when the application type is web
            if (client.ApplicationType == ApplicationTypes.Web)
            {
                foreach (var redirectUri in newClient.RedirectionUrls)
                {
                    if (!redirectUri.IsAbsoluteUri || !Uri.IsWellFormedUriString(redirectUri.AbsoluteUri, UriKind.Absolute))
                    {
                        var message = string.Format(Strings.TheRedirectUrlIsNotValid, redirectUri);
                        _logger.LogError(message);
                        return(new Option <Client> .Error(new ErrorDetails
                        {
                            Title = ErrorCodes.InvalidRedirectUri,
                            Detail = message,
                            Status = HttpStatusCode.BadRequest
                        }));
                    }

                    if (!string.IsNullOrWhiteSpace(redirectUri.Fragment))
                    {
                        var message = string.Format(Strings.TheRedirectUrlCannotContainsFragment, redirectUri);
                        _logger.LogError(message);
                        return(new Option <Client> .Error(
                                   new ErrorDetails
                        {
                            Title = ErrorCodes.InvalidRedirectUri,
                            Detail = message,
                            Status = HttpStatusCode.BadRequest
                        }));
                    }

                    client.RedirectionUrls = client.RedirectionUrls.Add(redirectUri);
                }
            }
            else
            {
                foreach (var redirectUri in newClient.RedirectionUrls)
                {
                    if (!Uri.IsWellFormedUriString(redirectUri.AbsoluteUri, UriKind.Absolute))
                    {
                        var message = string.Format(Strings.TheRedirectUrlIsNotValid, redirectUri);
                        _logger.LogError(message);
                        return(new Option <Client> .Error(
                                   new ErrorDetails
                        {
                            Title = ErrorCodes.InvalidRedirectUri,
                            Detail = message,
                            Status = HttpStatusCode.BadRequest
                        }));
                    }

                    client.RedirectionUrls = client.RedirectionUrls.Add(redirectUri);
                }
            }

            client.RequestObjectEncryptionAlg = newClient.RequestObjectEncryptionAlg;
            client.RequestObjectEncryptionEnc = newClient.RequestObjectEncryptionEnc;
            client.RequestObjectSigningAlg    = newClient.RequestObjectSigningAlg;
            client.RequestUris     = newClient.RequestUris;
            client.RequireAuthTime = newClient.RequireAuthTime;
            client.RequirePkce     = newClient.RequirePkce;

            client.UserInfoSignedResponseAlg = !string.IsNullOrWhiteSpace(newClient.UserInfoSignedResponseAlg)
                ? newClient.UserInfoSignedResponseAlg
                : SecurityAlgorithms.None;

            client.UserInfoEncryptedResponseAlg = !string.IsNullOrWhiteSpace(newClient.UserInfoEncryptedResponseAlg)
                ? newClient.UserInfoEncryptedResponseAlg
                : string.Empty;

            if (!string.IsNullOrWhiteSpace(client.UserInfoEncryptedResponseAlg))
            {
                client.UserInfoEncryptedResponseEnc = !string.IsNullOrWhiteSpace(newClient.UserInfoEncryptedResponseEnc)
                    ? newClient.UserInfoEncryptedResponseEnc
                    : SecurityAlgorithms.Aes128CbcHmacSha256;
            }

            client.RequestObjectSigningAlg = !string.IsNullOrWhiteSpace(newClient.RequestObjectSigningAlg)
                ? newClient.RequestObjectSigningAlg
                : string.Empty;

            client.RequestObjectEncryptionAlg = !string.IsNullOrWhiteSpace(newClient.RequestObjectEncryptionAlg)
                ? newClient.RequestObjectEncryptionAlg
                : string.Empty;

            if (!string.IsNullOrWhiteSpace(client.RequestObjectEncryptionAlg))
            {
                client.RequestObjectEncryptionEnc = !string.IsNullOrWhiteSpace(newClient.RequestObjectEncryptionEnc)
                    ? newClient.RequestObjectEncryptionEnc
                    : SecurityAlgorithms.Aes128CbcHmacSha256;
            }

            client.TokenEndPointAuthSigningAlg = !string.IsNullOrWhiteSpace(newClient.TokenEndPointAuthSigningAlg)
                ? newClient.TokenEndPointAuthSigningAlg
                : string.Empty;

            return(new Option <Client> .Result(client));
        }