private async Task UpdateRedirectUris(string clientId, ClientRegisteration registration) { var redirectUriResponse = await _clientUriStore.GetAsync(new PageRequest { Filter = $"{nameof(ClientUri.ClientId)} eq '{clientId}'" }).ConfigureAwait(false); foreach (var item in redirectUriResponse.Items) { if (!registration.RedirectUris.Any(u => u == item.Uri)) { await _clientUriStore.DeleteAsync(item.Id).ConfigureAwait(false); } } foreach (var redirectUri in registration.RedirectUris) { if (!redirectUriResponse.Items.Any(u => u.Uri == redirectUri)) { await _clientUriStore.CreateAsync(new ClientUri { ClientId = clientId, Id = Guid.NewGuid().ToString(), Kind = UriKinds.Cors | UriKinds.Redirect, Uri = redirectUri }).ConfigureAwait(false); } } }
public async Task CreateAsync_should_validate_native_redirect_uri_scheme(string redirectUri) { var sut = TestUtils.CreateTestServer(); sut.Services.GetRequiredService <TestUserService>() .SetTestUser(true, new Claim[] { new Claim("role", "Is4-Writer") }); var client = sut.CreateClient(); var registration = new ClientRegisteration { RedirectUris = new[] { redirectUri }, ApplicationType = "native" }; // not redirect uri using var request = new StringContent(JsonConvert.SerializeObject(registration), Encoding.UTF8, "application/json"); using var response = await client.PostAsync("/api/register", request); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); var error = JsonConvert.DeserializeObject <RegistrationProblemDetail>(content); Assert.Equal("invalid_redirect_uri", error.Error); var uri = new Uri(redirectUri); Assert.Equal($"Invalid RedirectUri '{redirectUri}'.Native client cannot use standard '{uri.Scheme}' scheme, you must use a custom scheme such as 'net.pipe' or 'net.tcp', or 'http' scheme with 'localhost' host.", error.Error_description); }
private async Task UpdateGrantTypes(string clientId, ClientRegisteration registration) { var grantTypeResponse = await _clientGrantTypeStore.GetAsync(new PageRequest { Filter = $"{nameof(ClientGrantType.ClientId)} eq '{clientId}'" }).ConfigureAwait(false); foreach (var item in grantTypeResponse.Items) { if (!registration.GrantTypes.Any(g => g == item.GrantType)) { await _clientGrantTypeStore.DeleteAsync(item.Id).ConfigureAwait(false); } } foreach (var grantType in registration.GrantTypes) { if (!grantTypeResponse.Items.Any(u => u.GrantType == grantType)) { await _clientGrantTypeStore.CreateAsync(new ClientGrantType { ClientId = clientId, Id = Guid.NewGuid().ToString(), GrantType = grantType }).ConfigureAwait(false); } } }
private async Task UpdatePropertyAsync(string clientId, ClientRegisteration registration, PageResponse <ClientProperty> propertiesResponse, string values, string key) { var property = propertiesResponse.Items.FirstOrDefault(p => p.Key == key); if ((registration.Contacts == null || !registration.Contacts.Any()) && property != null) { await _clientPropertyStore.DeleteAsync(property.Id).ConfigureAwait(false); } if (registration.Contacts == null || !registration.Contacts.Any()) { if (property == null) { await _clientPropertyStore.CreateAsync(new ClientProperty { ClientId = clientId, Id = Guid.NewGuid().ToString(), Key = key, Value = values }).ConfigureAwait(false); } else if (property.Value != values) { property.Value = values; await _clientPropertyStore.UpdateAsync(property).ConfigureAwait(false); } } }
private static void ValidateRedirectUri(ClientRegisteration registration, string uri, Uri redirectUri) { if (registration.ApplicationType == "web" && registration.GrantTypes.Contains("implicit")) { if (redirectUri.Scheme != "https") { throw new RegistrationException("invalid_redirect_uri", $"Invalid RedirectUri '{uri}'. Implicit client must use 'https' scheme only."); } if (redirectUri.Host.ToUpperInvariant() == "LOCALHOST") { throw new RegistrationException("invalid_redirect_uri", $"Invalid RedirectUri '{uri}'. Implicit client cannot use 'localhost' host."); } } if (registration.ApplicationType == "native") { if (redirectUri.Scheme == Uri.UriSchemeGopher || redirectUri.Scheme == Uri.UriSchemeHttps || redirectUri.Scheme == Uri.UriSchemeNews || redirectUri.Scheme == Uri.UriSchemeNntp) { throw new RegistrationException("invalid_redirect_uri", $"Invalid RedirectUri '{uri}'.Native client cannot use standard '{redirectUri.Scheme}' scheme, you must use a custom scheme such as 'net.pipe' or 'net.tcp', or 'http' scheme with 'localhost' host."); } if (redirectUri.Scheme == Uri.UriSchemeHttp && redirectUri.Host.ToUpperInvariant() != "LOCALHOST") { throw new RegistrationException("invalid_redirect_uri", $"Invalid RedirectUri '{uri}'.Only 'localhost' host is allowed for 'http' scheme and 'native' client."); } } }
private async Task UpdatePropertiesAsync(string clientId, ClientRegisteration registration) { var propertiesResponse = await _clientPropertyStore.GetAsync(new PageRequest { Filter = $"{nameof(ClientProperty.ClientId)} eq '{clientId}' and ({nameof(ClientProperty.Key)} eq 'contacts' or {nameof(ClientProperty.Key)} eq 'responseTypes')" }).ConfigureAwait(false); await UpdatePropertyAsync(clientId, registration, propertiesResponse, registration.Contacts != null?string.Join("; ", registration.Contacts) : null, "contacts").ConfigureAwait(false); await UpdatePropertyAsync(clientId, registration, propertiesResponse, registration.ResponseTypes != null?string.Join("; ", registration.ResponseTypes) : null, "responseType").ConfigureAwait(false); }
private void Validate(ClientRegisteration registration, Dictionary <string, object> discovery) { registration.GrantTypes ??= new List <string> { "authorization_code" }; if (registration.RedirectUris == null || !registration.RedirectUris.Any()) { throw new RegistrationException("invalid_redirect_uri", "RedirectUri is required."); } if (registration.ApplicationType != "web" && registration.ApplicationType != "native") { throw new RegistrationException("invalid_application_type", $"ApplicationType '{registration.ApplicationType}' is invalid. It must be 'web' or 'native'."); } var validationOptions = _identityServerOptions1.Validation; var redirectUriList = new List <Uri>(registration.RedirectUris.Count()); foreach (var uri in registration.RedirectUris) { if (validationOptions.InvalidRedirectUriPrefixes .Any(scheme => uri?.StartsWith(scheme, StringComparison.OrdinalIgnoreCase) == true)) { throw new RegistrationException("invalid_redirect_uri", $"RedirectUri '{uri}' uses invalid scheme. If this scheme should be allowed, then configure it via ValidationOptions."); } if (!Uri.TryCreate(uri, UriKind.Absolute, out var redirectUri)) { throw new RegistrationException("invalid_redirect_uri", $"RedirectUri '{uri}' is not valid."); } ValidateRedirectUri(registration, uri, redirectUri); redirectUriList.Add(redirectUri); } ValidateUris(redirectUriList, registration.LogoUris, "invalid_logo_uri", "LogoUri"); ValidateUris(redirectUriList, registration.PolicyUris, "invalid_policy_uri", "PolicyUri"); if (registration.GrantTypes == null || !registration.GrantTypes.Any()) { registration.GrantTypes = new[] { "authorization_code" }; } ValidateGrantType(registration.GrantTypes, discovery["grant_types_supported"] as IEnumerable <string>); if (registration.ResponseTypes == null || !registration.ResponseTypes.Any()) { registration.ResponseTypes = new[] { "code" }; } ValidateResponseType(registration.GrantTypes, registration.ResponseTypes, discovery["response_types_supported"] as IEnumerable <string>); }
private async Task UpdateClient(ClientRegisteration registration, Client client) { client.ClientUri = registration.ClientUris?.FirstOrDefault(u => u.Culture == null)?.Value ?? registration.ClientUris?.FirstOrDefault()?.Value; client.LogoUri = registration.LogoUris?.FirstOrDefault(u => u.Culture == null)?.Value ?? registration.LogoUris?.FirstOrDefault()?.Value; client.PolicyUri = registration.TosUris?.FirstOrDefault(u => u.Culture == null)?.Value ?? registration.TosUris?.FirstOrDefault()?.Value; client.TosUri = registration.TosUris?.FirstOrDefault(u => u.Culture == null)?.Value ?? registration.TosUris?.FirstOrDefault()?.Value; await _clientStore.UpdateAsync(client).ConfigureAwait(false); }
private void ValidateCaller(ClientRegisteration registration, HttpContext httpContext) { if (!(httpContext.User?.IsInRole(SharedConstants.WRITER) ?? false)) { var allowedContact = _dymamicClientRegistrationOptions.AllowedContacts?.FirstOrDefault(c => registration.Contacts?.Contains(c.Contact) ?? false); if (allowedContact == null) { throw new ForbiddenException(); } if (!allowedContact.AllowedHosts.Any(h => registration.RedirectUris.Select(u => new Uri(u)).Any(u => u.Host == h))) { throw new ForbiddenException(); } } }
public void WriteJson_should_serialize_localizable_properties() { var registration = new ClientRegisteration { ClientNames = new List <LocalizableProperty> { new LocalizableProperty { Culture = "fr-FR", Value = "test" } }, ApplicationType = "native", Contacts = new List <string> { "test" } }; var result = JsonConvert.SerializeObject(registration); Assert.Contains("client_name#fr-FR", result); }
public Task <ClientRegisteration> UpdateAsync(string clientId, [FromBody] ClientRegisteration registeration) => _registerClientService.UpdateRegistrationAsync(clientId, registeration, $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host}/");
public Task <ClientRegisteration> CreateAsync([FromBody] ClientRegisteration registeration) => _registerClientService.RegisterAsync(registeration, HttpContext);
public async Task DeleteAsync_should_delete_client() { var sut = TestUtils.CreateTestServer(); sut.Services.GetRequiredService <TestUserService>() .SetTestUser(true, new Claim[] { new Claim("role", "Is4-Writer") }); var client = sut.CreateClient(); var registration = new ClientRegisteration { ClientNames = new List <LocalizableProperty> { new LocalizableProperty { Value = "test" }, }, RedirectUris = new List <string> { "http://localhost" } }; using (var request = new StringContent(JsonConvert.SerializeObject(registration), Encoding.UTF8, "application/json")) { using var response = await client.PostAsync("/api/register", request); var content = await response.Content.ReadAsStringAsync(); registration = JsonConvert.DeserializeObject <ClientRegisteration>(content); Assert.Equal(HttpStatusCode.Created, response.StatusCode); Assert.NotNull(registration.RegistrationToken); } using (var message = new HttpRequestMessage { Method = HttpMethod.Delete, RequestUri = new Uri(registration.RegistrationUri) }) { message.Headers.Authorization = new AuthenticationHeaderValue("Bearer", registration.RegistrationToken); using var response = await client.SendAsync(message); Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); } using (var message = new HttpRequestMessage { Method = HttpMethod.Get, RequestUri = new Uri(registration.RegistrationUri) }) { message.Headers.Authorization = new AuthenticationHeaderValue("Bearer", registration.RegistrationToken); using var response = await client.SendAsync(message); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } }
public async Task CreateAsync_should_validate_caller() { using var sut = TestUtils.CreateTestServer(configurationOverrides: new Dictionary <string, string> { ["DynamicClientRegistrationOptions:AllowedContacts:0:Contact"] = "test", ["DynamicClientRegistrationOptions:AllowedContacts:0:AllowedHosts:0"] = "localhost", }); var client = sut.CreateClient(); var registration = new ClientRegisteration { ClientNames = new List <LocalizableProperty> { new LocalizableProperty { Value = "test" }, }, RedirectUris = new List <string> { "http://localhost" }, Contacts = new[] { "test" } }; using (var request = new StringContent(JsonConvert.SerializeObject(registration), Encoding.UTF8, "application/json")) { using var response = await client.PostAsync("/api/register", request); var content = await response.Content.ReadAsStringAsync(); var result = JsonConvert.DeserializeObject <ClientRegisteration>(content); Assert.Equal(HttpStatusCode.Created, response.StatusCode); Assert.NotNull(result.RegistrationToken); Assert.NotNull(result.RegistrationUri); } registration.RedirectUris = new[] { "http://forbidenn" }; using (var request = new StringContent(JsonConvert.SerializeObject(registration), Encoding.UTF8, "application/json")) { using var response = await client.PostAsync("/api/register", request); Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); } registration.Contacts = new[] { "forbidenn" }; using (var request = new StringContent(JsonConvert.SerializeObject(registration), Encoding.UTF8, "application/json")) { using var response = await client.PostAsync("/api/register", request); Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); } }
public async Task CreateAsync_should_validate_request() { using var sut = TestUtils.CreateTestServer(); sut.Services.GetRequiredService <TestUserService>() .SetTestUser(true, new Claim[] { new Claim("role", "Is4-Writer"), new Claim(JwtClaimTypes.Scope, SharedConstants.ADMINSCOPE) }); var client = sut.CreateClient(); var registration = new ClientRegisteration { }; // not redirect uri using (var request = new StringContent(JsonConvert.SerializeObject(registration), Encoding.UTF8, "application/json")) { using var response = await client.PostAsync("/api/register", request); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); var error = JsonConvert.DeserializeObject <RegistrationProblemDetail>(content); Assert.Equal("invalid_redirect_uri", error.Error); Assert.Equal("RedirectUri is required.", error.Error_description); } registration.RedirectUris = new List <string> { }; // empty redirect uris using (var request = new StringContent(JsonConvert.SerializeObject(registration), Encoding.UTF8, "application/json")) { using var response = await client.PostAsync("/api/register", request); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); var error = JsonConvert.DeserializeObject <RegistrationProblemDetail>(content); Assert.Equal("invalid_redirect_uri", error.Error); Assert.Equal("RedirectUri is required.", error.Error_description); } registration.RedirectUris = new List <string> { "test" }; // invalid uri prefix using (var request = new StringContent(JsonConvert.SerializeObject(registration), Encoding.UTF8, "application/json")) { using var response = await client.PostAsync("/api/register", request); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); var error = JsonConvert.DeserializeObject <RegistrationProblemDetail>(content); Assert.Equal("invalid_redirect_uri", error.Error); Assert.Equal("RedirectUri 'test' is not valid.", error.Error_description); } registration.RedirectUris = new List <string> { "ssh:test" }; // invalid uri prefix using (var request = new StringContent(JsonConvert.SerializeObject(registration), Encoding.UTF8, "application/json")) { using var response = await client.PostAsync("/api/register", request); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); var error = JsonConvert.DeserializeObject <RegistrationProblemDetail>(content); Assert.Equal("invalid_redirect_uri", error.Error); Assert.Equal("RedirectUri 'ssh:test' uses invalid scheme. If this scheme should be allowed, then configure it via ValidationOptions.", error.Error_description); } registration.RedirectUris = new List <string> { "http://localhost" }; registration.LogoUris = new List <LocalizableProperty> { new LocalizableProperty { Value = "test" } }; // invalid logo uris using (var request = new StringContent(JsonConvert.SerializeObject(registration), Encoding.UTF8, "application/json")) { using var response = await client.PostAsync("/api/register", request); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); var error = JsonConvert.DeserializeObject <RegistrationProblemDetail>(content); Assert.Equal("invalid_logo_uri", error.Error); Assert.Equal("LogoUri 'test' is not valid.", error.Error_description); } registration.LogoUris = new List <LocalizableProperty> { new LocalizableProperty { Value = "http://test" } }; // logo uri don't match redirect uri host using (var request = new StringContent(JsonConvert.SerializeObject(registration), Encoding.UTF8, "application/json")) { using var response = await client.PostAsync("/api/register", request); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); var error = JsonConvert.DeserializeObject <RegistrationProblemDetail>(content); Assert.Equal("invalid_logo_uri", error.Error); Assert.Equal("LogoUri 'http://test' host doesn't match a redirect uri host.", error.Error_description); } registration.LogoUris = null; registration.PolicyUris = new List <LocalizableProperty> { new LocalizableProperty { Value = "test" } }; // invalid logo uris using (var request = new StringContent(JsonConvert.SerializeObject(registration), Encoding.UTF8, "application/json")) { using var response = await client.PostAsync("/api/register", request); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); var error = JsonConvert.DeserializeObject <RegistrationProblemDetail>(content); Assert.Equal("invalid_policy_uri", error.Error); Assert.Equal("PolicyUri 'test' is not valid.", error.Error_description); } registration.PolicyUris = new List <LocalizableProperty> { new LocalizableProperty { Value = "http://test" } }; // policy uri don't match redirect uri host using (var request = new StringContent(JsonConvert.SerializeObject(registration), Encoding.UTF8, "application/json")) { using var response = await client.PostAsync("/api/register", request); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); var error = JsonConvert.DeserializeObject <RegistrationProblemDetail>(content); Assert.Equal("invalid_policy_uri", error.Error); Assert.Equal("PolicyUri 'http://test' host doesn't match a redirect uri host.", error.Error_description); } registration.PolicyUris = null; registration.GrantTypes = new[] { "invalid" }; // invalid grant type using (var request = new StringContent(JsonConvert.SerializeObject(registration), Encoding.UTF8, "application/json")) { using var response = await client.PostAsync("/api/register", request); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); var error = JsonConvert.DeserializeObject <RegistrationProblemDetail>(content); Assert.Equal("invalid_grant_type", error.Error); Assert.Equal("GrantType 'invalid' is not supported.", error.Error_description); } registration.GrantTypes = null; registration.ResponseTypes = new[] { "invalid" }; // invalid reponse type using (var request = new StringContent(JsonConvert.SerializeObject(registration), Encoding.UTF8, "application/json")) { using var response = await client.PostAsync("/api/register", request); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); var error = JsonConvert.DeserializeObject <RegistrationProblemDetail>(content); Assert.Equal("invalid_response_type", error.Error); Assert.Equal("ResponseType 'invalid' is not supported.", error.Error_description); } registration.GrantTypes = new[] { "implicit" }; registration.RedirectUris = new[] { "https://test" }; registration.ResponseTypes = new[] { "code" }; // invalid reponse type using (var request = new StringContent(JsonConvert.SerializeObject(registration), Encoding.UTF8, "application/json")) { using var response = await client.PostAsync("/api/register", request); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); var error = JsonConvert.DeserializeObject <RegistrationProblemDetail>(content); Assert.Equal("invalid_response_type", error.Error); Assert.Equal("No GrantType 'authorization_code' for ResponseType 'code' found in grant_types.", error.Error_description); } registration.GrantTypes = null; registration.ResponseTypes = new[] { "id_token" }; // reponse / grant type doesn't match using (var request = new StringContent(JsonConvert.SerializeObject(registration), Encoding.UTF8, "application/json")) { using var response = await client.PostAsync("/api/register", request); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); var error = JsonConvert.DeserializeObject <RegistrationProblemDetail>(content); Assert.Equal("invalid_response_type", error.Error); Assert.Equal("No GrantType 'implicit' for ResponseType 'id_token' found in grant_types.", error.Error_description); } registration.ResponseTypes = new[] { "token id_token" }; // reponse / grant type doesn't match using (var request = new StringContent(JsonConvert.SerializeObject(registration), Encoding.UTF8, "application/json")) { using var response = await client.PostAsync("/api/register", request); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); var error = JsonConvert.DeserializeObject <RegistrationProblemDetail>(content); Assert.Equal("invalid_response_type", error.Error); Assert.Equal("No GrantType 'implicit' for ResponseType 'token id_token' found in grant_types.", error.Error_description); } registration.ResponseTypes = new[] { "code token id_token" }; registration.GrantTypes = new[] { "implicit" }; // reponse / grant type doesn't match using (var request = new StringContent(JsonConvert.SerializeObject(registration), Encoding.UTF8, "application/json")) { using var response = await client.PostAsync("/api/register", request); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); var error = JsonConvert.DeserializeObject <RegistrationProblemDetail>(content); Assert.Equal("invalid_response_type", error.Error); Assert.Equal("No GrantType 'authorization_code' for ResponseType 'code token id_token' found in grant_types.", error.Error_description); } registration.ResponseTypes = new[] { "token" }; registration.RedirectUris = new[] { "http://test" }; // invalid scheme for grant type implicit using (var request = new StringContent(JsonConvert.SerializeObject(registration), Encoding.UTF8, "application/json")) { using var response = await client.PostAsync("/api/register", request); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); var error = JsonConvert.DeserializeObject <RegistrationProblemDetail>(content); Assert.Equal("invalid_redirect_uri", error.Error); Assert.Equal("Invalid RedirectUri 'http://test'. Implicit client must use 'https' scheme only.", error.Error_description); } registration.RedirectUris = new[] { "https://localhost" }; // invalid host for grant type implicit using (var request = new StringContent(JsonConvert.SerializeObject(registration), Encoding.UTF8, "application/json")) { using var response = await client.PostAsync("/api/register", request); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); var error = JsonConvert.DeserializeObject <RegistrationProblemDetail>(content); Assert.Equal("invalid_redirect_uri", error.Error); Assert.Equal("Invalid RedirectUri 'https://localhost'. Implicit client cannot use 'localhost' host.", error.Error_description); } registration.RedirectUris = new[] { "https://localhost" }; // invalid host for grant type implicit using (var request = new StringContent(JsonConvert.SerializeObject(registration), Encoding.UTF8, "application/json")) { using var response = await client.PostAsync("/api/register", request); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); var error = JsonConvert.DeserializeObject <RegistrationProblemDetail>(content); Assert.Equal("invalid_redirect_uri", error.Error); Assert.Equal("Invalid RedirectUri 'https://localhost'. Implicit client cannot use 'localhost' host.", error.Error_description); } registration.ResponseTypes = null; registration.GrantTypes = null; registration.ApplicationType = "native"; registration.RedirectUris = new[] { "http://test" }; // invalid host for native client using (var request = new StringContent(JsonConvert.SerializeObject(registration), Encoding.UTF8, "application/json")) { using var response = await client.PostAsync("/api/register", request); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); var error = JsonConvert.DeserializeObject <RegistrationProblemDetail>(content); Assert.Equal("invalid_redirect_uri", error.Error); Assert.Equal("Invalid RedirectUri 'http://test'.Only 'localhost' host is allowed for 'http' scheme and 'native' client.", error.Error_description); } registration.ApplicationType = "invalid"; // invalid application type using (var request = new StringContent(JsonConvert.SerializeObject(registration), Encoding.UTF8, "application/json")) { using var response = await client.PostAsync("/api/register", request); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); var error = JsonConvert.DeserializeObject <RegistrationProblemDetail>(content); Assert.Equal("invalid_application_type", error.Error); Assert.Equal("ApplicationType 'invalid' is invalid. It must be 'web' or 'native'.", error.Error_description); } }
public async Task CreateAsync_should_register_a_new_client() { var configuration = new Dictionary <string, string> { ["Seed"] = "false" }; using var sut = TestUtils.CreateTestServer(configurationOverrides: configuration); sut.Services.GetRequiredService <TestUserService>() .SetTestUser(true, new Claim[] { new Claim("role", "Is4-Writer"), new Claim(JwtClaimTypes.Scope, SharedConstants.ADMINSCOPE) }); var client = sut.CreateClient(); var registration = new ClientRegisteration { ClientNames = new List <LocalizableProperty> { new LocalizableProperty { Value = "test" }, }, RedirectUris = new List <string> { "http://localhost" } }; using (var request = new StringContent(JsonConvert.SerializeObject(registration), Encoding.UTF8, "application/json")) { using var response = await client.PostAsync("/api/register", request); var content = await response.Content.ReadAsStringAsync(); var result = JsonConvert.DeserializeObject <ClientRegisteration>(content); Assert.Equal(HttpStatusCode.Created, response.StatusCode); Assert.NotNull(result.RegistrationToken); Assert.NotNull(result.RegistrationUri); } registration.Jwks = new JsonWebKeys { Keys = new[] { new JsonWebKey { kty = "RSA", e = "AQAB", use = "sig", alg = "RS256", n = "qBulUDaYV027shwCq82LKIevXdQL2pCwXktQgf2TT3c496pxGdRuxcN_MHGKWNOGQsDLuAVk6NjxYF95obDUFrDiugMuXrvptPrTO8dzTX83k_6ngtjOtx2UrTk_7f0EYNrusykrsB-cOvCMREsfktlsavvMKBGrzpxaHlRxcSsMxzB0dddDSlH8mxlzOGcbBuvZnbNg0EUuQC4jvM9Gy6gUEcoU0S19XnUcgwLGLPfIX2dMO4FxTAsaaTYT7msxGMBNIVUTVnL0HctYr0YVYu0hD9rePnvxJ_-OwOdxIETQlR9vp61xFr4juzyyMWTrjCACxxLm-CyEQGjwx2YZaw" } } }; registration.RedirectUris = new List <string> { "https://localhost" }; registration.ClientNames = new List <LocalizableProperty> { new LocalizableProperty { Culture = "fr-FR", Value = "test" }, }; registration.ClientUris = new List <LocalizableProperty> { new LocalizableProperty { Value = "https://localhost" }, new LocalizableProperty { Culture = "fr-FR", Value = "https://localhost/fr-FR" }, }; registration.LogoUris = new List <LocalizableProperty> { new LocalizableProperty { Value = "https://localhost" }, new LocalizableProperty { Culture = "fr-FR", Value = "https://localhost/fr-FR" }, }; registration.PolicyUris = new List <LocalizableProperty> { new LocalizableProperty { Value = "https://localhost" }, new LocalizableProperty { Culture = "fr-FR", Value = "https://localhost/fr-FR" }, }; registration.TosUris = new List <LocalizableProperty> { new LocalizableProperty { Value = "https://localhost" }, new LocalizableProperty { Culture = "fr-FR", Value = "https://localhost/fr-FR" }, }; registration.JwksUri = "https://jwk"; using (var request = new StringContent(JsonConvert.SerializeObject(registration), Encoding.UTF8, "application/json")) { using var response = await client.PostAsync("/api/register", request); Assert.Equal(HttpStatusCode.Created, response.StatusCode); } }
/// <summary> /// Registers the asynchronous. /// </summary> /// <param name="registration">The client registration.</param> /// <param name="httpContext">The HTTP context.</param> /// <returns></returns> public async Task <ClientRegisteration> RegisterAsync(ClientRegisteration registration, HttpContext httpContext) { var uri = $"{httpContext.Request.Scheme}://{httpContext.Request.Host}/"; var discovery = await _discoveryResponseGenerator.CreateDiscoveryDocumentAsync(uri, uri).ConfigureAwait(false); ValidateCaller(registration, httpContext); Validate(registration, discovery); var clientName = registration.ClientNames?.FirstOrDefault(n => n.Culture == null)?.Value ?? registration.ClientNames?.FirstOrDefault()?.Value ?? Guid.NewGuid().ToString(); var existing = await _clientStore.GetAsync(clientName, null).ConfigureAwait(false); registration.Id = existing != null?Guid.NewGuid().ToString() : clientName; registration.Id = registration.Id.Contains(' ') ? Guid.NewGuid().ToString() : registration.Id; var secret = Guid.NewGuid().ToString(); var serializerSettings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }; var jwkKeys = registration.Jwks?.Keys; var sercretList = jwkKeys != null?jwkKeys.Select(k => new ClientSecret { Id = Guid.NewGuid().ToString(), Type = IdentityServer4.IdentityServerConstants.SecretTypes.JsonWebKey, Value = JsonConvert.SerializeObject(k, serializerSettings) }).ToList() : new List <ClientSecret>(); sercretList.Add(new ClientSecret { Id = Guid.NewGuid().ToString(), Type = IdentityServer4.IdentityServerConstants.SecretTypes.SharedSecret, Value = secret }); var client = new Client { AllowedGrantTypes = registration.GrantTypes.Select(grant => new ClientGrantType { Id = Guid.NewGuid().ToString(), GrantType = grant }).ToList(), AccessTokenLifetime = _defaultValues.AccessTokenLifetime, AbsoluteRefreshTokenLifetime = _defaultValues.AbsoluteRefreshTokenLifetime, AccessTokenType = (int)_defaultValues.AccessTokenType, AllowOfflineAccess = registration.ApplicationType == "web" && registration.GrantTypes.Contains("refresh_token"), AllowAccessTokensViaBrowser = registration.ApplicationType == "web", AllowedScopes = new List <ClientScope> { new ClientScope { Id = Guid.NewGuid().ToString(), Scope = "openid" }, new ClientScope { Id = Guid.NewGuid().ToString(), Scope = "profile" }, new ClientScope { Id = Guid.NewGuid().ToString(), Scope = "address" }, new ClientScope { Id = Guid.NewGuid().ToString(), Scope = "email" }, new ClientScope { Id = Guid.NewGuid().ToString(), Scope = "phone" } }, AllowRememberConsent = _defaultValues.AllowRememberConsent, AllowPlainTextPkce = _defaultValues.AllowPlainTextPkce, AlwaysIncludeUserClaimsInIdToken = _defaultValues.AlwaysIncludeUserClaimsInIdToken, AlwaysSendClientClaims = _defaultValues.AlwaysSendClientClaims, AuthorizationCodeLifetime = _defaultValues.AuthorizationCodeLifetime, BackChannelLogoutSessionRequired = !string.IsNullOrEmpty(_defaultValues.BackChannelLogoutUri), BackChannelLogoutUri = _defaultValues.BackChannelLogoutUri, ClientClaimsPrefix = _defaultValues.ClientClaimsPrefix, ClientName = clientName, ClientSecrets = sercretList, ClientUri = registration.ClientUris?.FirstOrDefault(u => u.Culture == null)?.Value ?? registration.ClientUris?.FirstOrDefault()?.Value, ConsentLifetime = _defaultValues.ConsentLifetime, Description = _defaultValues.Description, DeviceCodeLifetime = _defaultValues.DeviceCodeLifetime, Enabled = true, EnableLocalLogin = _defaultValues.EnableLocalLogin, FrontChannelLogoutSessionRequired = !string.IsNullOrEmpty(_defaultValues.FrontChannelLogoutUri), FrontChannelLogoutUri = _defaultValues.FrontChannelLogoutUri, Id = registration.Id, IdentityTokenLifetime = _defaultValues.IdentityTokenLifetime, IncludeJwtId = _defaultValues.IncludeJwtId, LogoUri = registration.LogoUris?.FirstOrDefault(u => u.Culture == null)?.Value ?? registration.LogoUris?.FirstOrDefault()?.Value, NonEditable = false, PairWiseSubjectSalt = _defaultValues.PairWiseSubjectSalt, ProtocolType = _defaultValues.ProtocolType, RedirectUris = registration.RedirectUris.Select(u => new ClientUri { Id = Guid.NewGuid().ToString(), Kind = UriKinds.Cors | UriKinds.Redirect, Uri = u }).ToList(), RefreshTokenExpiration = (int)_defaultValues.RefreshTokenExpiration, RefreshTokenUsage = (int)_defaultValues.RefreshTokenUsage, RequireClientSecret = false, RequireConsent = _defaultValues.RequireConsent, RequirePkce = false, Resources = registration.ClientNames.Where(n => n.Culture != null) .Select(n => new ClientLocalizedResource { Id = Guid.NewGuid().ToString(), CultureId = n.Culture, ResourceKind = EntityResourceKind.DisplayName, Value = n.Value }) .Union(registration.ClientUris != null ? registration.ClientUris.Where(u => u.Culture != null).Select(u => new ClientLocalizedResource { Id = Guid.NewGuid().ToString(), CultureId = u.Culture, ResourceKind = EntityResourceKind.ClientUri, Value = u.Value }) : new List <ClientLocalizedResource>(0)) .Union(registration.LogoUris != null ? registration.LogoUris.Where(u => u.Culture != null).Select(u => new ClientLocalizedResource { Id = Guid.NewGuid().ToString(), CultureId = u.Culture, ResourceKind = EntityResourceKind.LogoUri, Value = u.Value }) : new List <ClientLocalizedResource>(0)) .Union(registration.PolicyUris != null ? registration.PolicyUris.Where(u => u.Culture != null).Select(u => new ClientLocalizedResource { Id = Guid.NewGuid().ToString(), CultureId = u.Culture, ResourceKind = EntityResourceKind.PolicyUri, Value = u.Value }) : new List <ClientLocalizedResource>(0)) .Union(registration.TosUris != null ? registration.TosUris.Where(u => u.Culture != null).Select(u => new ClientLocalizedResource { Id = Guid.NewGuid().ToString(), CultureId = u.Culture, ResourceKind = EntityResourceKind.TosUri, Value = u.Value }) : new List <ClientLocalizedResource>(0)) .ToList(), Properties = new List <ClientProperty> { new ClientProperty { Id = Guid.NewGuid().ToString(), Key = "applicationType", Value = registration.ApplicationType } }.Union(registration.Contacts != null ? new List <ClientProperty> { new ClientProperty { Id = Guid.NewGuid().ToString(), Key = "contacts", Value = string.Join("; ", registration.Contacts) } } : new List <ClientProperty>(0)) .ToList(), SlidingRefreshTokenLifetime = _defaultValues.SlidingRefreshTokenLifetime, UpdateAccessTokenClaimsOnRefresh = _defaultValues.UpdateAccessTokenClaimsOnRefresh, UserCodeType = _defaultValues.UserCodeType, UserSsoLifetime = _defaultValues.UserSsoLifetime, PolicyUri = registration.TosUris?.FirstOrDefault(u => u.Culture == null)?.Value ?? registration.TosUris?.FirstOrDefault()?.Value, TosUri = registration.TosUris?.FirstOrDefault(u => u.Culture == null)?.Value ?? registration.TosUris?.FirstOrDefault()?.Value, }; client.RegistrationToken = Guid.NewGuid(); await _clientStore.CreateAsync(client).ConfigureAwait(false); registration.RegistrationToken = client.RegistrationToken.ToString(); registration.RegistrationUri = $"{discovery["registration_endpoint"]}/{client.Id}"; registration.JwksUri = discovery["jwks_uri"].ToString(); registration.ClientSecret = secret; registration.ClientSecretExpireAt = 0; return(registration); }
public async Task GetAsync_should_return_registration() { using var sut = TestUtils.CreateTestServer(); sut.Services.GetRequiredService <TestUserService>() .SetTestUser(true, new Claim[] { new Claim(JwtClaimTypes.Role, "Is4-Writer"), new Claim(JwtClaimTypes.Scope, SharedConstants.ADMINSCOPE) }); var client = sut.CreateClient(); var registration = new ClientRegisteration { ClientNames = new List <LocalizableProperty> { new LocalizableProperty { Value = "test" }, }, RedirectUris = new List <string> { "https://localhost" }, ClientUris = new List <LocalizableProperty> { new LocalizableProperty { Value = "https://localhost" }, new LocalizableProperty { Culture = "fr-FR", Value = "https://localhost/fr-FR" }, }, LogoUris = new List <LocalizableProperty> { new LocalizableProperty { Value = "https://localhost" }, new LocalizableProperty { Culture = "fr-FR", Value = "https://localhost/fr-FR" }, }, PolicyUris = new List <LocalizableProperty> { new LocalizableProperty { Value = "https://localhost" }, new LocalizableProperty { Culture = "fr-FR", Value = "https://localhost/fr-FR" }, }, TosUris = new List <LocalizableProperty> { new LocalizableProperty { Value = "https://localhost" }, new LocalizableProperty { Culture = "fr-FR", Value = "https://localhost/fr-FR" }, }, ResponseTypes = new[] { "code" } }; using (var request = new StringContent(JsonConvert.SerializeObject(registration), Encoding.UTF8, "application/json")) { using var response = await client.PostAsync("/api/register", request); var content = await response.Content.ReadAsStringAsync(); registration = JsonConvert.DeserializeObject <ClientRegisteration>(content); Assert.Equal(HttpStatusCode.Created, response.StatusCode); Assert.NotNull(registration.RegistrationToken); } using (var message = new HttpRequestMessage { Method = HttpMethod.Get, RequestUri = new Uri(registration.RegistrationUri) }) { message.Headers.Authorization = new AuthenticationHeaderValue("Bearer", registration.RegistrationToken); using var response = await client.SendAsync(message); var content = await response.Content.ReadAsStringAsync(); var result = JsonConvert.DeserializeObject <ClientRegisteration>(content); Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Null(result.RegistrationToken); Assert.Null(result.RegistrationUri); } }
/// <summary> /// Updates the registration asynchronous. /// </summary> /// <param name="clientId">The client identifier.</param> /// <param name="registration">The client.</param> /// <param name="uri">The URI.</param> /// <returns></returns> /// <exception cref="NotImplementedException"></exception> public async Task <ClientRegisteration> UpdateRegistrationAsync(string clientId, ClientRegisteration registration, string uri) { var client = await GetClientAsync(clientId).ConfigureAwait(false); var discovery = await _discoveryResponseGenerator.CreateDiscoveryDocumentAsync(uri, uri).ConfigureAwait(false); Validate(registration, discovery); await UpdateClient(registration, client).ConfigureAwait(false); await UpdateRedirectUris(clientId, registration).ConfigureAwait(false); await UpdateGrantTypes(clientId, registration).ConfigureAwait(false); var resourceResponse = await _clientResourceStore.GetAsync(new PageRequest { Filter = $"{nameof(ClientGrantType.ClientId)} eq '{clientId}'" }).ConfigureAwait(false); var items = resourceResponse.Items; var clientNameList = registration.ClientNames?.Where(n => n.Culture != null) ?? Array.Empty <LocalizableProperty>(); var clientUriList = registration.ClientUris?.Where(u => u.Culture != null) ?? Array.Empty <LocalizableProperty>(); var logoUriList = registration.LogoUris?.Where(u => u.Culture != null) ?? Array.Empty <LocalizableProperty>(); var policyUriList = registration.PolicyUris?.Where(u => u.Culture != null) ?? Array.Empty <LocalizableProperty>(); var tosUriList = registration.TosUris?.Where(u => u.Culture != null) ?? Array.Empty <LocalizableProperty>(); foreach (var item in items) { await DeleteItemAsync(clientNameList, clientUriList, logoUriList, policyUriList, tosUriList, item).ConfigureAwait(false); } await AddResourceAsync(clientId, items, clientNameList).ConfigureAwait(false); await AddResourceAsync(clientId, items, clientUriList).ConfigureAwait(false); await AddResourceAsync(clientId, items, logoUriList).ConfigureAwait(false); await AddResourceAsync(clientId, items, policyUriList).ConfigureAwait(false); await AddResourceAsync(clientId, items, tosUriList).ConfigureAwait(false); await UpdatePropertiesAsync(clientId, registration).ConfigureAwait(false); registration.RegistrationToken = null; registration.RegistrationUri = null; registration.JwksUri = discovery["jwks_uri"].ToString(); registration.ClientSecret = client.ClientSecrets.FirstOrDefault()?.Value; registration.ClientSecretExpireAt = client.ClientSecrets.Any() ? (int?)0 : null; return(registration); }