Skip to content

Commit

Permalink
Add PostLogoutRedirectUri property in the OktaMvcOptions Class (Core) (
Browse files Browse the repository at this point in the history
…#68)

- Add PostLogoutRedirectUri property in the OktaMvcOptions class (core).
- Encapsulate OIDC options creation/config to improve code testability.
  • Loading branch information
laura-rodriguez committed Mar 20, 2019
1 parent cacfce7 commit 278780d
Show file tree
Hide file tree
Showing 7 changed files with 234 additions and 71 deletions.
@@ -0,0 +1,48 @@
// <copyright file="OpenIdConnectAuthenticationOptionsBuilderShould.cs" company="Okta, Inc">
// Copyright (c) 2018-present Okta, Inc. All rights reserved.
// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information.
// </copyright>

using System.Collections.Generic;
using FluentAssertions;
using Microsoft.Owin.Security.OpenIdConnect;
using Okta.AspNet.Abstractions;
using Xunit;

namespace Okta.AspNet.Test
{
public class OpenIdConnectAuthenticationOptionsBuilderShould
{
[Fact]
public void BuildOpenIdConnectAuthenticationOptionsCorrectly()
{
var oktaMvcOptions = new OktaMvcOptions()
{
PostLogoutRedirectUri = "http://postlogout.com",
OktaDomain = "http://myoktadomain.com",
ClientId = "foo",
ClientSecret = "bar",
RedirectUri = "/redirectUri",
Scope = new List<string> { "openid", "profile", "email" },
};

var notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = null,
};

var oidcOptions = OpenIdConnectAuthenticationOptionsBuilder.BuildOpenIdConnectAuthenticationOptions(
oktaMvcOptions,
notifications);

oidcOptions.ClientId.Should().Be(oktaMvcOptions.ClientId);
oidcOptions.ClientSecret.Should().Be(oktaMvcOptions.ClientSecret);
oidcOptions.PostLogoutRedirectUri.Should().Be(oktaMvcOptions.PostLogoutRedirectUri);

var issuer = UrlHelper.CreateIssuerUrl(oktaMvcOptions.OktaDomain, oktaMvcOptions.AuthorizationServerId);
oidcOptions.Authority.Should().Be(issuer);
oidcOptions.RedirectUri.Should().Be(oktaMvcOptions.RedirectUri);
oidcOptions.Scope.Should().Be(string.Join(" ", oktaMvcOptions.Scope));
}
}
}
40 changes: 5 additions & 35 deletions Okta.AspNet/OktaMiddlewareExtensions.cs
Expand Up @@ -81,45 +81,15 @@ private static void AddJwtBearerAuthentication(IAppBuilder app, OktaWebApiOption

private static void AddOpenIdConnectAuthentication(IAppBuilder app, OktaMvcOptions options)
{
var issuer = UrlHelper.CreateIssuerUrl(options.OktaDomain, options.AuthorizationServerId);
var httpClient = new HttpClient(new UserAgentHandler("okta-aspnet", typeof(OktaMiddlewareExtensions).Assembly.GetName().Version));

var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(
issuer + "/.well-known/openid-configuration",
new OpenIdConnectConfigurationRetriever(),
new HttpDocumentRetriever(httpClient));

var tokenValidationParameters = new DefaultTokenValidationParameters(options, issuer)
{
NameClaimType = "name",
ValidAudience = options.ClientId,
};

var tokenExchanger = new TokenExchanger(options, issuer, configurationManager);

// Stop the default behavior of remapping JWT claim names to legacy MS/SOAP claim names
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

var definedScopes = options.Scope?.ToArray() ?? OktaDefaults.Scope;
var scopeString = string.Join(" ", definedScopes);

app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
var notifications = new OpenIdConnectAuthenticationNotifications
{
ClientId = options.ClientId,
ClientSecret = options.ClientSecret,
Authority = issuer,
RedirectUri = options.RedirectUri,
ResponseType = OpenIdConnectResponseType.CodeIdToken,
Scope = scopeString,
PostLogoutRedirectUri = options.PostLogoutRedirectUri,
TokenValidationParameters = tokenValidationParameters,
SecurityTokenValidator = new StrictSecurityTokenValidator(),
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = tokenExchanger.ExchangeCodeForTokenAsync,
RedirectToIdentityProvider = BeforeRedirectToIdentityProviderAsync,
},
});
RedirectToIdentityProvider = BeforeRedirectToIdentityProviderAsync,
};

app.UseOpenIdConnectAuthentication(OpenIdConnectAuthenticationOptionsBuilder.BuildOpenIdConnectAuthenticationOptions(options, notifications));
}

private static Task BeforeRedirectToIdentityProviderAsync(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> n)
Expand Down
62 changes: 62 additions & 0 deletions Okta.AspNet/OpenIdConnectAuthenticationOptionsBuilder.cs
@@ -0,0 +1,62 @@
// <copyright file="OpenIdConnectAuthenticationOptionsBuilder.cs" company="Okta, Inc">
// Copyright (c) 2018-present Okta, Inc. All rights reserved.
// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information.
// </copyright>

using System.Linq;
using System.Net.Http;
using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.Owin.Security.OpenIdConnect;
using Okta.AspNet.Abstractions;

namespace Okta.AspNet
{
public class OpenIdConnectAuthenticationOptionsBuilder
{
/// <summary>
/// Creates a new instance of OpenIdConnectAuthenticationOptions.
/// </summary>
/// <param name="oktaMvcOptions">The <see cref="OktaMvcOptions"/> options.</param>
/// <param name="notifications">The OpenIdConnectAuthenticationNotifications notifications.</param>
/// <returns>A new instance of OpenIdConnectAuthenticationOptions.</returns>
public static OpenIdConnectAuthenticationOptions BuildOpenIdConnectAuthenticationOptions(OktaMvcOptions oktaMvcOptions, OpenIdConnectAuthenticationNotifications notifications)
{
var issuer = UrlHelper.CreateIssuerUrl(oktaMvcOptions.OktaDomain, oktaMvcOptions.AuthorizationServerId);
var httpClient = new HttpClient(new UserAgentHandler("okta-aspnet", typeof(OktaMiddlewareExtensions).Assembly.GetName().Version));

var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(
issuer + "/.well-known/openid-configuration",
new OpenIdConnectConfigurationRetriever(),
new HttpDocumentRetriever(httpClient));

var tokenValidationParameters = new DefaultTokenValidationParameters(oktaMvcOptions, issuer)
{
NameClaimType = "name",
ValidAudience = oktaMvcOptions.ClientId,
};

var tokenExchanger = new TokenExchanger(oktaMvcOptions, issuer, configurationManager);
var definedScopes = oktaMvcOptions.Scope?.ToArray() ?? OktaDefaults.Scope;
var scopeString = string.Join(" ", definedScopes);

return new OpenIdConnectAuthenticationOptions
{
ClientId = oktaMvcOptions.ClientId,
ClientSecret = oktaMvcOptions.ClientSecret,
Authority = issuer,
RedirectUri = oktaMvcOptions.RedirectUri,
ResponseType = OpenIdConnectResponseType.CodeIdToken,
Scope = scopeString,
PostLogoutRedirectUri = oktaMvcOptions.PostLogoutRedirectUri,
TokenValidationParameters = tokenValidationParameters,
SecurityTokenValidator = new StrictSecurityTokenValidator(),
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = tokenExchanger.ExchangeCodeForTokenAsync,
RedirectToIdentityProvider = notifications.RedirectToIdentityProvider,
},
};
}
}
}
52 changes: 52 additions & 0 deletions Okta.AspNetCore.Test/OpenIdConnectOptionsHelperShould.cs
@@ -0,0 +1,52 @@
// <copyright file="OpenIdConnectOptionsHelperShould.cs" company="Okta, Inc">
// Copyright (c) 2018-present Okta, Inc. All rights reserved.
// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information.
// </copyright>

using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Okta.AspNet.Abstractions;
using Xunit;

namespace Okta.AspNetCore.Test
{
public class OpenIdConnectOptionsHelperShould
{
[Fact]
public void SetOpenIdConnectsOptionsCorrectly()
{
var oktaMvcOptions = new OktaMvcOptions
{
PostLogoutRedirectUri = "http://foo.postlogout.com",
AuthorizationServerId = "bar",
ClientId = "foo",
ClientSecret = "baz",
OktaDomain = "http://myoktadomain.com",
GetClaimsFromUserInfoEndpoint = true,
CallbackPath = "/somecallbackpath",
Scope = new List<string> { "openid", "profile", "email" },
};

var events = new OpenIdConnectEvents() { OnRedirectToIdentityProvider = null };

var oidcOptions = new OpenIdConnectOptions();

OpenIdConnectOptionsHelper.ConfigureOpenIdConnectOptions(oktaMvcOptions, events, oidcOptions);

oidcOptions.ClientId.Should().Be(oktaMvcOptions.ClientId);
oidcOptions.ClientSecret.Should().Be(oktaMvcOptions.ClientSecret);
oidcOptions.SignedOutRedirectUri.Should().Be(oktaMvcOptions.PostLogoutRedirectUri);
oidcOptions.GetClaimsFromUserInfoEndpoint.Should().Be(oktaMvcOptions.GetClaimsFromUserInfoEndpoint);
oidcOptions.CallbackPath.Value.Should().Be(oktaMvcOptions.CallbackPath);

var issuer = UrlHelper.CreateIssuerUrl(oktaMvcOptions.OktaDomain, oktaMvcOptions.AuthorizationServerId);
oidcOptions.Authority.Should().Be(issuer);

oidcOptions.Scope.ToList().Should().BeEquivalentTo(oktaMvcOptions.Scope);
oidcOptions.CallbackPath.Value.Should().Be(oktaMvcOptions.CallbackPath);
oidcOptions.Events.OnRedirectToIdentityProvider.Should().BeNull();
}
}
}
41 changes: 5 additions & 36 deletions Okta.AspNetCore/OktaAuthenticationOptionsExtensions.cs
Expand Up @@ -5,13 +5,10 @@

using System;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Okta.AspNet.Abstractions;

namespace Okta.AspNetCore
Expand All @@ -32,42 +29,14 @@ public static AuthenticationBuilder AddOktaMvc(this AuthenticationBuilder builde

private static AuthenticationBuilder AddCodeFlow(AuthenticationBuilder builder, OktaMvcOptions options)
{
var issuer = UrlHelper.CreateIssuerUrl(options.OktaDomain, options.AuthorizationServerId);

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

builder.AddOpenIdConnect(oidcOptions =>
var events = new OpenIdConnectEvents
{
oidcOptions.ClientId = options.ClientId;
oidcOptions.ClientSecret = options.ClientSecret;
oidcOptions.Authority = issuer;
oidcOptions.CallbackPath = new PathString(options.CallbackPath);
oidcOptions.SignedOutCallbackPath = new PathString(OktaDefaults.SignOutCallbackPath);
oidcOptions.ResponseType = OpenIdConnectResponseType.Code;
oidcOptions.GetClaimsFromUserInfoEndpoint = options.GetClaimsFromUserInfoEndpoint;
oidcOptions.SecurityTokenValidator = new StrictSecurityTokenValidator();
oidcOptions.SaveTokens = true;
oidcOptions.UseTokenLifetime = false;
oidcOptions.BackchannelHttpHandler = new UserAgentHandler("okta-aspnetcore", typeof(OktaAuthenticationOptionsExtensions).Assembly.GetName().Version);
var hasDefinedScopes = options.Scope?.Any() ?? false;
if (hasDefinedScopes)
{
oidcOptions.Scope.Clear();
foreach (var scope in options.Scope)
{
oidcOptions.Scope.Add(scope);
}
}
OnRedirectToIdentityProvider = BeforeRedirectToIdentityProviderAsync,
};

oidcOptions.TokenValidationParameters = new DefaultTokenValidationParameters(options, issuer)
{
ValidAudience = options.ClientId,
NameClaimType = "name",
};
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

oidcOptions.Events.OnRedirectToIdentityProvider = BeforeRedirectToIdentityProviderAsync;
});
builder.AddOpenIdConnect(oidcOptions => OpenIdConnectOptionsHelper.ConfigureOpenIdConnectOptions(options, events, oidcOptions));

return builder;
}
Expand Down
2 changes: 2 additions & 0 deletions Okta.AspNetCore/OktaMvcOptions.cs
Expand Up @@ -15,6 +15,8 @@ public class OktaMvcOptions : AspNet.Abstractions.OktaWebOptions

public string CallbackPath { get; set; } = OktaDefaults.CallbackPath;

public string PostLogoutRedirectUri { get; set; }

public IList<string> Scope { get; set; } = OktaDefaults.Scope;

public bool GetClaimsFromUserInfoEndpoint { get; set; } = false;
Expand Down
60 changes: 60 additions & 0 deletions Okta.AspNetCore/OpenIdConnectOptionsHelper.cs
@@ -0,0 +1,60 @@
// <copyright file="OpenIdConnectOptionsHelper.cs" company="Okta, Inc">
// Copyright (c) 2018-present Okta, Inc. All rights reserved.
// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information.
// </copyright>

using System.Linq;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Http;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Okta.AspNet.Abstractions;

namespace Okta.AspNetCore
{
public class OpenIdConnectOptionsHelper
{
/// <summary>
/// Configure an OpenIdConnectOptions object based on user's configuration.
/// </summary>
/// <param name="oktaMvcOptions">The <see cref="OktaMvcOptions"/> options.</param>
/// <param name="events">The OpenIdConnect events.</param>
/// <param name="oidcOptions">The OpenIdConnectOptions to configure.</param>
public static void ConfigureOpenIdConnectOptions(OktaMvcOptions oktaMvcOptions, OpenIdConnectEvents events, OpenIdConnectOptions oidcOptions)
{
var issuer = UrlHelper.CreateIssuerUrl(oktaMvcOptions.OktaDomain, oktaMvcOptions.AuthorizationServerId);

oidcOptions.ClientId = oktaMvcOptions.ClientId;
oidcOptions.ClientSecret = oktaMvcOptions.ClientSecret;
oidcOptions.Authority = issuer;
oidcOptions.CallbackPath = new PathString(oktaMvcOptions.CallbackPath);
oidcOptions.SignedOutCallbackPath = new PathString(OktaDefaults.SignOutCallbackPath);
oidcOptions.SignedOutRedirectUri = oktaMvcOptions.PostLogoutRedirectUri;
oidcOptions.ResponseType = OpenIdConnectResponseType.Code;
oidcOptions.GetClaimsFromUserInfoEndpoint = oktaMvcOptions.GetClaimsFromUserInfoEndpoint;
oidcOptions.SecurityTokenValidator = new StrictSecurityTokenValidator();
oidcOptions.SaveTokens = true;
oidcOptions.UseTokenLifetime = false;
oidcOptions.BackchannelHttpHandler = new UserAgentHandler(
"okta-aspnetcore",
typeof(OktaAuthenticationOptionsExtensions).Assembly.GetName().Version);

var hasDefinedScopes = oktaMvcOptions.Scope?.Any() ?? false;
if (hasDefinedScopes)
{
oidcOptions.Scope.Clear();
foreach (var scope in oktaMvcOptions.Scope)
{
oidcOptions.Scope.Add(scope);
}
}

oidcOptions.TokenValidationParameters = new DefaultTokenValidationParameters(oktaMvcOptions, issuer)
{
ValidAudience = oktaMvcOptions.ClientId,
NameClaimType = "name",
};

oidcOptions.Events.OnRedirectToIdentityProvider = events.OnRedirectToIdentityProvider;
}
}
}

0 comments on commit 278780d

Please sign in to comment.