From c4b787cb2f18abd5d82d6884d4449a3cffe47742 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Rodr=C3=ADguez?= Date: Fri, 23 Aug 2019 16:14:37 -0400 Subject: [PATCH] Add support for social login. (#95) Add support for external identity providers. --- Okta.AspNet/OktaMiddlewareExtensions.cs | 22 ++++++++++------ .../Controllers/AccountController.cs | 14 +++++++++++ .../MiddlewareShould.cs | 13 ++++++++++ .../OktaAuthenticationOptionsExtensions.cs | 8 ++++++ docs/aspnet4x-mvc.md | 25 +++++++++++++++++++ docs/aspnetcore-mvc.md | 22 ++++++++++++++++ 6 files changed, 97 insertions(+), 7 deletions(-) diff --git a/Okta.AspNet/OktaMiddlewareExtensions.cs b/Okta.AspNet/OktaMiddlewareExtensions.cs index 75c8b82..cca3f37 100644 --- a/Okta.AspNet/OktaMiddlewareExtensions.cs +++ b/Okta.AspNet/OktaMiddlewareExtensions.cs @@ -92,26 +92,34 @@ private static void AddOpenIdConnectAuthentication(IAppBuilder app, OktaMvcOptio app.UseOpenIdConnectAuthentication(OpenIdConnectAuthenticationOptionsBuilder.BuildOpenIdConnectAuthenticationOptions(options, notifications)); } - private static Task BeforeRedirectToIdentityProviderAsync(RedirectToIdentityProviderNotification n) + private static Task BeforeRedirectToIdentityProviderAsync(RedirectToIdentityProviderNotification redirectToIdentityProviderNotification) { // If signing out, add the id_token_hint - if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout) + if (redirectToIdentityProviderNotification.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout) { - if (n.OwinContext.Authentication.User.FindFirst("id_token") != null) + if (redirectToIdentityProviderNotification.OwinContext.Authentication.User.FindFirst("id_token") != null) { - n.ProtocolMessage.IdTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token").Value; + redirectToIdentityProviderNotification.ProtocolMessage.IdTokenHint = redirectToIdentityProviderNotification.OwinContext.Authentication.User.FindFirst("id_token").Value; } } // Add sessionToken to provide custom login - if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Authentication) + if (redirectToIdentityProviderNotification.ProtocolMessage.RequestType == OpenIdConnectRequestType.Authentication) { var sessionToken = string.Empty; - n.OwinContext.Authentication.AuthenticationResponseChallenge?.Properties?.Dictionary?.TryGetValue("sessionToken", out sessionToken); + redirectToIdentityProviderNotification.OwinContext.Authentication.AuthenticationResponseChallenge?.Properties?.Dictionary?.TryGetValue("sessionToken", out sessionToken); if (!string.IsNullOrEmpty(sessionToken)) { - n.ProtocolMessage.SetParameter("sessionToken", sessionToken); + redirectToIdentityProviderNotification.ProtocolMessage.SetParameter("sessionToken", sessionToken); + } + + var idpId = string.Empty; + redirectToIdentityProviderNotification.OwinContext.Authentication.AuthenticationResponseChallenge?.Properties?.Dictionary?.TryGetValue("idp", out idpId); + + if (!string.IsNullOrEmpty(idpId)) + { + redirectToIdentityProviderNotification.ProtocolMessage.SetParameter("idp", idpId); } } diff --git a/Okta.AspNetCore.Mvc.IntegrationTest/Controllers/AccountController.cs b/Okta.AspNetCore.Mvc.IntegrationTest/Controllers/AccountController.cs index c15a3a2..4103b0b 100644 --- a/Okta.AspNetCore.Mvc.IntegrationTest/Controllers/AccountController.cs +++ b/Okta.AspNetCore.Mvc.IntegrationTest/Controllers/AccountController.cs @@ -24,6 +24,20 @@ public IActionResult Login() return RedirectToAction("Index", "Home"); } + public IActionResult LoginWithIdp() + { + if (!HttpContext.User.Identity.IsAuthenticated) + { + var properties = new AuthenticationProperties(); + properties.Items.Add("idp", "foo"); + properties.RedirectUri = "/Home/"; + + return Challenge(properties, OktaDefaults.MvcAuthenticationScheme); + } + + return RedirectToAction("Index", "Home"); + } + [HttpPost] public IActionResult Logout() { diff --git a/Okta.AspNetCore.Mvc.IntegrationTest/MiddlewareShould.cs b/Okta.AspNetCore.Mvc.IntegrationTest/MiddlewareShould.cs index 62152fb..1064842 100644 --- a/Okta.AspNetCore.Mvc.IntegrationTest/MiddlewareShould.cs +++ b/Okta.AspNetCore.Mvc.IntegrationTest/MiddlewareShould.cs @@ -49,6 +49,19 @@ public async Task RedirectWhenAccessToProtectedRouteWithoutSignedInAsync() } } + [Fact] + public async Task IncludeIdpInAuthorizeUrlAsync() + { + var loginWithIdpEndpoint = string.Format("{0}/Account/LoginWithIdp", BaseUrl); + HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, loginWithIdpEndpoint); + using (var client = _server.CreateClient()) + { + var response = await client.GetAsync(loginWithIdpEndpoint); + Assert.True(response.StatusCode == System.Net.HttpStatusCode.Found); + Assert.Contains("idp=foo", response.Headers.Location.AbsoluteUri); + } + } + public void Dispose() { _server.Dispose(); diff --git a/Okta.AspNetCore/OktaAuthenticationOptionsExtensions.cs b/Okta.AspNetCore/OktaAuthenticationOptionsExtensions.cs index 7bbf495..c463593 100644 --- a/Okta.AspNetCore/OktaAuthenticationOptionsExtensions.cs +++ b/Okta.AspNetCore/OktaAuthenticationOptionsExtensions.cs @@ -52,6 +52,14 @@ private static Task BeforeRedirectToIdentityProviderAsync(RedirectContext contex } } + if (context.Properties.Items.TryGetValue("idp", out var idpId)) + { + if (!string.IsNullOrEmpty(idpId)) + { + context.ProtocolMessage.SetParameter("idp", idpId); + } + } + return Task.CompletedTask; } diff --git a/docs/aspnet4x-mvc.md b/docs/aspnet4x-mvc.md index 5b14260..be0b380 100644 --- a/docs/aspnet4x-mvc.md +++ b/docs/aspnet4x-mvc.md @@ -78,6 +78,31 @@ public class Startup > Note: If you are using role-based authorization and you need to redirect unauthorized users to an access-denied page or similar, check out [CookieAuthenticationProvider.ApplyRedirect](https://docs.microsoft.com/en-us/previous-versions/aspnet/mt152260(v%3Dvs.113)). +## Login with an external identity provider + +Add the following action in your controller: + +```csharp +public ActionResult LoginWithIdp(string idp) +{ + if (!HttpContext.User.Identity.IsAuthenticated) + { + var properties = new AuthenticationProperties(); + properties.Dictionary.Add("idp", idp); + properties.RedirectUri = "/Home/About"; + + HttpContext.GetOwinContext().Authentication.Challenge(properties, + OktaDefaults.MvcAuthenticationType); + + return new HttpUnauthorizedResult(); + } + + return RedirectToAction("Index", "Home"); +} +``` + +The Okta.AspNet library will include your identity provider id in the authorize URL and the user will prompted with the identity provider login. For more information, check out our guides to [add an external identity provider](https://developer.okta.com/docs/guides/add-an-external-idp/). + # Configuration Reference The `OktaMvcOptions` class configures the Okta middleware. You can see all the available options in the table below: diff --git a/docs/aspnetcore-mvc.md b/docs/aspnetcore-mvc.md index 6d6fdaf..d5596d8 100644 --- a/docs/aspnetcore-mvc.md +++ b/docs/aspnetcore-mvc.md @@ -81,6 +81,28 @@ public void ConfigureServices(IServiceCollection services) ``` > Note: If you are using role-based authorization and you need to redirect unauthorized users to an access-denied page or similar, check out [CookieAuthenticationOptions.AccessDeniedPath](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.cookies.cookieauthenticationoptions.accessdeniedpath?view=aspnetcore-2.2). +## Login with an external identity provider + +Add the following action in your controller: + +```csharp +public IActionResult SignInWithIdp(string idp) +{ + if (!HttpContext.User.Identity.IsAuthenticated) + { + var properties = new AuthenticationProperties(); + properties.Items.Add("idp", idp); + properties.RedirectUri = "/Home/"; + + return Challenge(properties, OktaDefaults.MvcAuthenticationScheme); + } + + return RedirectToAction("Index", "Home"); +} +``` + +The Okta.AspNetCore library will include your identity provider id in the authorize URL and the user will prompted with the identity provider login. For more information, check out our guides to [add an external identity provider](https://developer.okta.com/docs/guides/add-an-external-idp/). + # Configuration Reference The `OktaMvcOptions` class configures the Okta middleware. You can see all the available options in the table below: