public void ProcessOidcAuthorizationResponse_FailedTokenRequest(string response, HttpStatusCode httpStatusCode, Type exceptionType, string message) { var context = RequestContext.GetContext(); OidcIdentityProvider idProvider; OidcIdentityProviderUser idProviderUser; UserAccount userAccount; string tenantName = GetCurrentTenantName(); CreateEntityModel("*****@*****.**", true, out idProvider, out idProviderUser, out userAccount); var code = Guid.NewGuid().ToString(); // Mock config provider and http client var errorResponseMessage = new HttpResponseMessage { Content = new StringContent(response), StatusCode = httpStatusCode }; var mockRepo = new MockRepository(MockBehavior.Strict); var configUrl = idProvider.OidcIdentityProviderConfigurationUrl; var configManager = mockRepo.Create <IOpenIdConnectConfigurationManager>(); configManager.Setup(w => w.GetIdentityProviderConfigurationAsync(configUrl)).Returns(Task.FromResult(_oidcConfig)); configManager.Setup(w => w.RemoveIdentityProviderConfiguration(configUrl)); var httpClient = mockRepo.Create <IHttpClient>(); httpClient.Setup(w => w.PostAsync(new Uri(_oidcConfig.TokenEndpoint), It.IsAny <HttpContent>())) .Returns(Task.FromResult(errorResponseMessage)); var oidcLoginHandler = new OpenIdConnectLoginHandler(configManager.Object); var idpLoginRequest = new IdentityProviderLoginRequest { Tenant = context.Tenant.Name, IdentityProviderId = idProvider.Id, RedirectUrl = "https://test.com/login/callback" }; var baseUri = new Uri("https://test.com"); // Get code auth uri, get state var uri = Task.Run(() => oidcLoginHandler.GetAuthorizationCodeRequestUrl(idpLoginRequest, baseUri)).Result; var queryValues = uri.ParseQueryString(); var state = queryValues["state"]; var validatedState = oidcLoginHandler.ValidateAuthState(state); Assert.That( async() => await oidcLoginHandler.ProcessOidcAuthorizationResponse(tenantName, code, validatedState, baseUri, httpClient.Object), Throws.TypeOf(exceptionType).And.Message.EqualTo(message)); mockRepo.VerifyAll(); }
/// <summary> /// Gets the authorization code request URL for the specified identity provider. /// </summary> /// <param name="idpLoginRequest">The oidc provider.</param> /// <param name="requestBaseUrl">The base url message.</param> /// <returns>Task<Uri>.</returns> /// <exception cref="System.ArgumentNullException"> /// </exception> public async Task <Uri> GetAuthorizationCodeRequestUrl(IdentityProviderLoginRequest idpLoginRequest, Uri requestBaseUrl) { if (idpLoginRequest == null) { throw new ArgumentNullException(nameof(idpLoginRequest)); } if (requestBaseUrl == null) { throw new ArgumentNullException(nameof(requestBaseUrl)); } if (string.IsNullOrWhiteSpace(idpLoginRequest.Tenant)) { throw new ArgumentException(@"The tenant is invalid.", nameof(idpLoginRequest)); } if (idpLoginRequest.IdentityProviderId <= 0) { throw new ArgumentException(@"The identity provider is invalid.", nameof(idpLoginRequest)); } if (string.IsNullOrWhiteSpace(idpLoginRequest.RedirectUrl)) { throw new ArgumentException(@"The redirect url is invalid.", nameof(idpLoginRequest)); } long tenantId; long oidcProviderId; string oidcConfigurationUrl; string oidcClientId; bool alwaysPrompt; using (new SecurityBypassContext()) using (new TenantAdministratorContext(idpLoginRequest.Tenant)) { var oidcIdentityProvider = ReadiNow.Model.Entity.Get <OidcIdentityProvider>(idpLoginRequest.IdentityProviderId, GetOidcProviderFieldsToLoad()); if (oidcIdentityProvider == null) { throw new AuthenticationException("The identity provider does not exist."); } ValidateOidcProviderFields(oidcIdentityProvider); // Store any required entity model fields upfront. // Any code running after the await statement may run a different thread tenantId = RequestContext.TenantId; oidcProviderId = oidcIdentityProvider.Id; oidcClientId = oidcIdentityProvider.OidcClientId; oidcConfigurationUrl = oidcIdentityProvider.OidcIdentityProviderConfigurationUrl; alwaysPrompt = oidcIdentityProvider.OidcAlwaysPrompt ?? true; } OpenIdConnectConfiguration oidcConfig; try { // Get the configuration oidcConfig = await _configurationManager.GetIdentityProviderConfigurationAsync(oidcConfigurationUrl); } catch (Exception ex) { throw new OidcProviderInvalidConfigurationException(ex); } var oidcProtocolValidator = new OpenIdConnectProtocolValidator(); // Create authorization state var authStateObject = new OpenIdConnectAuthorizationState { Timestamp = DateTime.UtcNow.Ticks, RedirectUrl = idpLoginRequest.RedirectUrl, IdentityProviderId = oidcProviderId, TenantId = tenantId, Nonce = oidcProtocolValidator.GenerateNonce() }; // Serialize and encrypt state var stateJson = JSON.Serialize(authStateObject); var cryptoProvider = new EncodingCryptoProvider(); var encryptedState = cryptoProvider.EncryptAndEncode(stateJson); // Create code request oidc message var oidcMessage = new OpenIdConnectMessage { ClientId = oidcClientId, IssuerAddress = oidcConfig.AuthorizationEndpoint, RedirectUri = GetOidcAuthResponseUri(requestBaseUrl, idpLoginRequest.Tenant), Scope = "openid email", ResponseType = "code", State = encryptedState, Nonce = authStateObject.Nonce, Prompt = alwaysPrompt ? "login" : null }; // Get request url return(new Uri(oidcMessage.CreateAuthenticationRequestUrl())); }
public void ProcessOidcAuthorizationResponse_InvalidUser(string test) { var context = RequestContext.GetContext(); OidcIdentityProvider idProvider = null; OidcIdentityProviderUser idProviderUser; UserAccount userAccount; string tenantName = GetCurrentTenantName(); switch (test) { case "NoIdpUser": // Create user account with a different name CreateEntityModel("testUser", true, out idProvider, out idProviderUser, out userAccount); break; case "NoUserAccount": // Create idp user with matching name but without associated account CreateEntityModelNoUserAccount("*****@*****.**", out idProvider, out idProviderUser); break; case "DisabledUserAccount": // Create user account with a disabled account CreateEntityModel("*****@*****.**", false, out idProvider, out idProviderUser, out userAccount); break; } var code = Guid.NewGuid().ToString(); // Mock config provider and http client var expectedTokenRequestMessage = new OpenIdConnectMessage { GrantType = "authorization_code", Code = code, ClientSecret = Factory.SecuredData.Read((Guid)idProvider.OidcClientSecretSecureId), ClientId = idProvider.OidcClientId, RedirectUri = "https://test.com/spapi/data/v1/login/oidc/authresponse/" + tenantName }; var validResponseMessage = new HttpResponseMessage { Content = new StringContent(_openIdTokenResponse) }; var mockRepo = new MockRepository(MockBehavior.Strict); var configUrl = idProvider.OidcIdentityProviderConfigurationUrl; var configManager = mockRepo.Create <IOpenIdConnectConfigurationManager>(); configManager.Setup(w => w.GetIdentityProviderConfigurationAsync(configUrl)).Returns(Task.FromResult(_oidcConfig)); var httpClient = mockRepo.Create <IHttpClient>(); httpClient.Setup(w => w.PostAsync(new Uri(_oidcConfig.TokenEndpoint), It.Is <HttpContent>(c => ValidateTokenRequestMessage(expectedTokenRequestMessage, c)))) .Returns(Task.FromResult(validResponseMessage)); var oidcLoginHandler = new OpenIdConnectLoginHandler(configManager.Object, true); var idpLoginRequest = new IdentityProviderLoginRequest { Tenant = context.Tenant.Name, IdentityProviderId = idProvider.Id, RedirectUrl = "https://test.com/login/callback/" }; var baseUri = new Uri("https://test.com"); // Get code auth uri, get state var uri = Task.Run(() => oidcLoginHandler.GetAuthorizationCodeRequestUrl(idpLoginRequest, baseUri)).Result; var queryValues = uri.ParseQueryString(); var state = queryValues["state"]; var validatedState = oidcLoginHandler.ValidateAuthState(state); Assert.That( async() => await oidcLoginHandler.ProcessOidcAuthorizationResponse(tenantName, code, validatedState, baseUri, httpClient.Object), Throws.TypeOf <AuthenticationException>().And.Message.EqualTo("The request context could not be found. The user name may be incorrect or the account may be locked, disabled or expired.")); mockRepo.VerifyAll(); }
public void ProcessOidcAuthorizationResponse_EnabledTokenValidation() { var context = RequestContext.GetContext(); OidcIdentityProvider idProvider; OidcIdentityProviderUser idProviderUser; UserAccount userAccount; string tenantName = GetCurrentTenantName(); CreateEntityModel("*****@*****.**", true, out idProvider, out idProviderUser, out userAccount); var code = Guid.NewGuid().ToString(); // Mock config provider and http client var expectedTokenRequestMessage = new OpenIdConnectMessage { GrantType = "authorization_code", Code = code, ClientSecret = Factory.SecuredData.Read((Guid)idProvider.OidcClientSecretSecureId), ClientId = idProvider.OidcClientId, RedirectUri = "https://test.com/spapi/data/v1/login/oidc/authresponse/" + tenantName }; var validResponseMessage = new HttpResponseMessage { Content = new StringContent(_openIdTokenResponse) }; var mockRepo = new MockRepository(MockBehavior.Strict); var configUrl = idProvider.OidcIdentityProviderConfigurationUrl; var configManager = mockRepo.Create <IOpenIdConnectConfigurationManager>(); configManager.Setup(w => w.GetIdentityProviderConfigurationAsync(configUrl)).Returns(Task.FromResult(_oidcConfig)); configManager.Setup(w => w.RemoveIdentityProviderConfiguration(configUrl)); var httpClient = mockRepo.Create <IHttpClient>(); httpClient.Setup(w => w.PostAsync(new Uri(_oidcConfig.TokenEndpoint), It.Is <HttpContent>(c => ValidateTokenRequestMessage(expectedTokenRequestMessage, c)))) .Returns(Task.FromResult(validResponseMessage)); var oidcLoginHandler = new OpenIdConnectLoginHandler(configManager.Object); var idpLoginRequest = new IdentityProviderLoginRequest { Tenant = context.Tenant.Name, IdentityProviderId = idProvider.Id, RedirectUrl = "https://test.com/login/callback" }; var baseUri = new Uri("https://test.com"); // Get code auth uri, get state var uri = Task.Run(() => oidcLoginHandler.GetAuthorizationCodeRequestUrl(idpLoginRequest, baseUri)).Result; var queryValues = uri.ParseQueryString(); var state = queryValues["state"]; var validatedState = oidcLoginHandler.ValidateAuthState(state); Assert.That( async() => await oidcLoginHandler.ProcessOidcAuthorizationResponse(tenantName, code, validatedState, baseUri, httpClient.Object), Throws.TypeOf <AuthenticationException>()); }
public void GetAuthorizationCodeRequestUrl() { var context = RequestContext.GetContext(); OidcIdentityProvider idProvider; OidcIdentityProviderUser idProviderUser; UserAccount userAccount; // Setup entity model CreateEntityModel("IDP User" + Guid.NewGuid(), false, out idProvider, out idProviderUser, out userAccount); // Create mock var mockRepo = new MockRepository(MockBehavior.Strict); var configUrl = idProvider.OidcIdentityProviderConfigurationUrl; var configManager = mockRepo.Create <IOpenIdConnectConfigurationManager>(); configManager.Setup(w => w.GetIdentityProviderConfigurationAsync(configUrl)).Returns(Task.FromResult(_oidcConfig)); var idpLoginRequest = new IdentityProviderLoginRequest { Tenant = context.Tenant.Name, IdentityProviderId = idProvider.Id, RedirectUrl = "https://test.com/login/callback" }; var baseUri = new Uri("https://test.com"); var oidcLoginHandler = new OpenIdConnectLoginHandler(configManager.Object); var timeStampBefore = DateTime.UtcNow; var uri = Task.Run(() => oidcLoginHandler.GetAuthorizationCodeRequestUrl(idpLoginRequest, baseUri)).Result; var timeStampAfter = DateTime.UtcNow; // Validate url Assert.AreEqual("rndev20adfs.sp.local", uri.Host); Assert.AreEqual("/adfs/oauth2/authorize/", uri.AbsolutePath); var queryValues = uri.ParseQueryString(); Assert.AreEqual(idProvider.OidcClientId, queryValues["client_id"], "The client_id is invalid"); Assert.AreEqual("https://test.com/spapi/data/v1/login/oidc/authresponse/" + context.Tenant.Name.ToLowerInvariant(), queryValues["redirect_uri"], "The redirect_uri is invalid"); Assert.AreEqual("openid email", queryValues["scope"], "The scope is invalid"); Assert.AreEqual("code", queryValues["response_type"], "The response_type is invalid"); Assert.AreEqual("login", queryValues["prompt"], "The prompt is invalid"); var nonce = queryValues["nonce"]; Assert.IsNotNullOrEmpty(nonce, "The nonce is invalid."); var state = queryValues["state"]; Assert.IsNotNullOrEmpty(state, "The state is invalid."); var validatedState = oidcLoginHandler.ValidateAuthState(state); Assert.AreEqual(context.Tenant.Id, validatedState.TenantId, "The state TenantId is invalid"); Assert.AreEqual(idProvider.Id, validatedState.IdentityProviderId, "The state IdentityProviderId is invalid"); Assert.AreEqual(nonce, validatedState.Nonce, "The state Nonce is invalid"); Assert.AreEqual(idpLoginRequest.RedirectUrl, validatedState.RedirectUrl, "The state RedirectUrl is invalid"); var timeStamp = new DateTime(validatedState.Timestamp, DateTimeKind.Utc); // Verify timestamp is between times Assert.GreaterOrEqual(timeStamp, timeStampBefore); Assert.LessOrEqual(timeStamp, timeStampAfter); mockRepo.VerifyAll(); }
/// <inheritdoc/> public ClientResponse <LoginResponse> ReconcileJWT(IdentityProviderLoginRequest request) { return(client.ReconcileJWTAsync(request).GetAwaiter().GetResult()); }
/// <inheritdoc/> public ClientResponse <LoginResponse> IdentityProviderLogin(IdentityProviderLoginRequest request) { return(client.IdentityProviderLoginAsync(request).GetAwaiter().GetResult()); }