예제 #1
0
        public void ProcessOidcAuthorizationResponse_InvalidAuthState()
        {
            var    mockRepo         = new MockRepository(MockBehavior.Loose);
            var    configManager    = mockRepo.Create <IOpenIdConnectConfigurationManager>();
            var    oidcLoginHandler = new OpenIdConnectLoginHandler(configManager.Object);
            string tenantName       = GetCurrentTenantName();

            var authState1 = new OpenIdConnectAuthorizationState
            {
                TenantId           = RequestContext.TenantId,
                Timestamp          = DateTime.UtcNow.Ticks,
                IdentityProviderId = 1000,
                Nonce       = "xx",
                RedirectUrl = "http://test.com"
            };

            Assert.That(
                async() =>
                await oidcLoginHandler.ProcessOidcAuthorizationResponse(tenantName, "code", authState1, new Uri("http://test.com"), new BasicHttpClient()),
                Throws.TypeOf <AuthenticationException>().And.Message.EqualTo("The identity provider does not exist."));

            var authState2 = new OpenIdConnectAuthorizationState
            {
                TenantId           = 100,
                Timestamp          = DateTime.UtcNow.Ticks,
                IdentityProviderId = 1000,
                Nonce       = "xx",
                RedirectUrl = "http://test.com"
            };

            Assert.That(
                async() =>
                await oidcLoginHandler.ProcessOidcAuthorizationResponse(tenantName, "code", authState2, new Uri("http://test.com"), new BasicHttpClient()),
                Throws.TypeOf <AuthenticationException>().And.Message.EqualTo("The tenant is invalid."));
        }
예제 #2
0
        public void ProcessOidcAuthorizationResponse_InvalidProviderSetup(string clientId, string clientSecret, string configUrl, string claim, bool enabled)
        {
            var    mockRepo         = new MockRepository(MockBehavior.Loose);
            var    configManager    = mockRepo.Create <IOpenIdConnectConfigurationManager>();
            var    oidcLoginHandler = new OpenIdConnectLoginHandler(configManager.Object);
            string tenantName       = GetCurrentTenantName();

            var provider = new OidcIdentityProvider
            {
                Name = "ID Provider " + Guid.NewGuid(),
                IsProviderEnabled = enabled,
                OidcClientId      = clientId,
                OidcClientSecret  = clientSecret,
                OidcIdentityProviderConfigurationUrl = configUrl,
                OidcUserIdentityClaim = claim
            };

            var authState = new OpenIdConnectAuthorizationState
            {
                TenantId           = RequestContext.TenantId,
                IdentityProviderId = provider.Id
            };

            Assert.That(
                async() =>
                await oidcLoginHandler.ProcessOidcAuthorizationResponse(tenantName, "code", authState, new Uri("http://test.com"), new BasicHttpClient()),
                Throws.TypeOf <AuthenticationException>());
        }
예제 #3
0
        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();
        }
예제 #4
0
        public void ProcessOidcAuthorizationResponse_InvalidParams()
        {
            var mockRepo         = new MockRepository(MockBehavior.Loose);
            var configManager    = mockRepo.Create <IOpenIdConnectConfigurationManager>();
            var oidcLoginHandler = new OpenIdConnectLoginHandler(configManager.Object);

            Assert.That(
                async() =>
                await oidcLoginHandler.ProcessOidcAuthorizationResponse(null, null, new OpenIdConnectAuthorizationState(), new Uri("http://test.com"), new BasicHttpClient()),
                Throws.TypeOf <ArgumentNullException>().And.Property("ParamName").EqualTo("tenant"));

            Assert.That(
                async() =>
                await oidcLoginHandler.ProcessOidcAuthorizationResponse("EDC", null, new OpenIdConnectAuthorizationState(), new Uri("http://test.com"), new BasicHttpClient()),
                Throws.TypeOf <ArgumentNullException>().And.Property("ParamName").EqualTo("code"));

            Assert.That(
                async() =>
                await oidcLoginHandler.ProcessOidcAuthorizationResponse("EDC", string.Empty, new OpenIdConnectAuthorizationState(), new Uri("http://test.com"), new BasicHttpClient()),
                Throws.TypeOf <ArgumentNullException>().And.Property("ParamName").EqualTo("code"));

            Assert.That(
                async() =>
                await oidcLoginHandler.ProcessOidcAuthorizationResponse("EDC", "code", null, new Uri("http://test.com"), new BasicHttpClient()),
                Throws.TypeOf <ArgumentNullException>().And.Property("ParamName").EqualTo("authState"));

            Assert.That(
                async() =>
                await oidcLoginHandler.ProcessOidcAuthorizationResponse("EDC", "code", new OpenIdConnectAuthorizationState(), null, new BasicHttpClient()),
                Throws.TypeOf <ArgumentNullException>().And.Property("ParamName").EqualTo("requestBaseUrl"));

            Assert.That(
                async() =>
                await oidcLoginHandler.ProcessOidcAuthorizationResponse("EDC", "code", new OpenIdConnectAuthorizationState(), new Uri("http://test.com"), null),
                Throws.TypeOf <ArgumentNullException>().And.Property("ParamName").EqualTo("httpClient"));
        }
예제 #5
0
        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();
        }
예제 #6
0
        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>());
        }
예제 #7
0
        // ReSharper disable once InconsistentNaming
        public async Task <IHttpActionResult> OidcAuthResponseCallback(string tenant, string code = null, string state = null, string error = null, string error_description = null)
        {
            OpenIdConnectAuthorizationState authState = null;

            try
            {
                if (!string.IsNullOrWhiteSpace(error))
                {
                    return(BadRequest($"An error occurred during authorization. Error:{error}. Description:{error_description}"));
                }

                if (string.IsNullOrWhiteSpace(tenant))
                {
                    throw new WebArgumentNullException("tenant", "The tenant was not specified");
                }

                if (string.IsNullOrWhiteSpace(code))
                {
                    throw new WebArgumentNullException("code", "The code was not specified");
                }

                if (string.IsNullOrWhiteSpace(state))
                {
                    throw new WebArgumentNullException("state", "The state was not specified");
                }

                // For some reason ADFS is replacing the + signs (even the encoded ones) with spaces, so we undo it.
                // State is Base64 encoded so it should not contain spaces.
                if (!string.IsNullOrWhiteSpace(state))
                {
                    state = state.Replace(' ', '+');
                }

                var oidcLoginHandler = new OpenIdConnectLoginHandler(OpenIdConnectConfigurationManager);
                if (oidcLoginHandler.IsTokenValidationDisabled)
                {
                    EventLog.Application.WriteError("OpenIdConnectLoginHandler has token validation disabled. This is not be used in production.");
                    throw new AuthenticationException();
                }

                // Validate the state.
                authState = oidcLoginHandler.ValidateAuthState(state);

                using (var httpClient = new BasicHttpClient())
                {
                    // Process the authorization response.
                    var result = await oidcLoginHandler.ProcessOidcAuthorizationResponse(tenant, code, authState, new Uri(Request.RequestUri.GetLeftPart(UriPartial.Authority)), httpClient);

                    CookieHelper.CreateAuthenticationAndXsrfCookies(authState.TenantId, authState.IdentityProviderId, result.IdentityProviderUserName, result.RequestContextData.Identity.Id, true);
                }

                return(Redirect(authState.RedirectUrl));
            }
            catch (Exception exception)
            {
                var oidcConfigException = exception as OidcProviderInvalidConfigurationException;

                EventLog.Application.WriteError("Failed to process oidc authorization response. Error: {0}", exception.ToString());

                CookieHelper.DeleteAuthenticationAndXsrfCookies();
                if (!string.IsNullOrWhiteSpace(authState?.RedirectUrl))
                {
                    string delimiter = authState.RedirectUrl.Contains("?") ? "&" : "?";
                    string errorType = oidcConfigException != null ? "idpconfigerror" : "autherror";
                    return(Redirect(authState.RedirectUrl + delimiter + "error=" + errorType));
                }

                return(Unauthorized());
            }
        }