Skip to content

Commit

Permalink
Jmprieur/easy auth without test project (#700)
Browse files Browse the repository at this point in the history
* First step of the integration of Easy-auth with Microsoft.Identity.Web
* Updating the EasyAuth code and adding debugging capabilities
* Improving the developer experience.

Co-authored-by: jennyf19 <jeferrie@microsoft.com>

* Improvements:
- in the authentication handler, processes both v1.0 and v2.0 IdTokens
- Fixing CA warnings
- Adding a page (Debugging) which explains how to debug the EasyAuth integration locally.

Co-authored-by: jennyf19 <jeferrie@microsoft.com>
  • Loading branch information
jmprieur and jennyf19 authored Oct 20, 2020
1 parent 100f4e7 commit b9de3cc
Show file tree
Hide file tree
Showing 17 changed files with 914 additions and 75 deletions.
4 changes: 2 additions & 2 deletions ProjectTemplates/configuration.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"B2C_Authority": "https://fabrikamb2c.b2clogin.com/fabrikamb2c.onmicrosoft.com/b2c_1_susi",

"B2C_Client_ClientId": "fdb91ff5-5ce6-41f3-bdbd-8267c817015d",
"B2C_Client_ClientSecret": "[Copy the client secret added to the app from the Azure portal]",
"B2C_Client_Port": "44365",

"B2C_WebApi_ClientId": "90c0fe63-bcf2-44d5-8fb7-b8bbc0b29dc6",
Expand All @@ -19,11 +18,12 @@
"AAD_Authority": "https://login.microsoftonline.com/msidentitysamplestesting.onmicrosoft.com",

"AAD_Client_ClientId": "86699d80-dd21-476a-bcd1-7c1a3d471f75",
"AAD_Client_ClientSecret": "[Copy the client secret added to the app from the Azure portal]",
"AAD_Client_Port": "44357",

"AAD_WebApi_ClientId": "a4c2469b-cf84-4145-8f5f-cb7bacf814bc",
"AAD_WebApi_Port": "44351",
"B2C_Client_ClientSecret": "[Copy the client secret added to the app from the Azure portal]",
"AAD_Client_ClientSecret": "[Copy the client secret added to the app from the Azure portal]",
"AAD_WebApi_ClientSecret": "[Copy the client secret added to the app from the Azure portal]"
},
"Projects": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,22 @@ public IActionResult Challenge(
[HttpGet("{scheme?}")]
public IActionResult SignOut([FromRoute] string scheme)
{
scheme ??= OpenIdConnectDefaults.AuthenticationScheme;
var callbackUrl = Url.Page("/Account/SignedOut", pageHandler: null, values: null, protocol: Request.Scheme);
return SignOut(
new AuthenticationProperties
{
RedirectUri = callbackUrl,
},
CookieAuthenticationDefaults.AuthenticationScheme,
scheme);
if (AppServicesAuthenticationInformation.IsAppServicesAadAuthenticationEnabled)
{
return LocalRedirect(AppServicesAuthenticationInformation.LogoutUrl);
}
else
{
scheme ??= OpenIdConnectDefaults.AuthenticationScheme;
var callbackUrl = Url.Page("/Account/SignedOut", pageHandler: null, values: null, protocol: Request.Scheme);
return SignOut(
new AuthenticationProperties
{
RedirectUri = callbackUrl,
},
CookieAuthenticationDefaults.AuthenticationScheme,
scheme);
}
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Microsoft.AspNetCore.Authentication;

namespace Microsoft.Identity.Web
{
/// <summary>
/// Extension methods related to App Services authentication (Easy Auth).
/// </summary>
public static class AppServicesAuthenticationBuilderExtensions
{
/// <summary>
/// Add authentication with App Services.
/// </summary>
/// <param name="builder">Authentication builder.</param>
/// <returns>The builder, to chain commands.</returns>
public static AuthenticationBuilder AddAppServicesAuthentication(
this AuthenticationBuilder builder)
{
if (builder is null)
{
throw new System.ArgumentNullException(nameof(builder));
}

builder.AddScheme<AppServicesAuthenticationOptions, AppServicesAuthenticationHandler>(
AppServicesAuthenticationDefaults.AuthenticationScheme,
AppServicesAuthenticationDefaults.AuthenticationScheme,
options => { });

return builder;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

namespace Microsoft.Identity.Web
{
/// <summary>
/// Default values related to AppServiceAuthentication handler.
/// </summary>
public class AppServicesAuthenticationDefaults
{
/// <summary>
/// The default value used for AppServiceAuthenticationOptions.AuthenticationScheme.
/// </summary>
public const string AuthenticationScheme = "AppServicesAuthentication";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Linq;
using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.JsonWebTokens;

namespace Microsoft.Identity.Web
{
/// <summary>
/// App service authentication handler.
/// </summary>
public class AppServicesAuthenticationHandler : AuthenticationHandler<AppServicesAuthenticationOptions>
{
/// <summary>
/// Constructor for the AppServiceAuthenticationHandler.
/// Note the parameters are required by the base class.
/// </summary>
/// <param name="options">App service authentication options.</param>
/// <param name="logger">Logger factory.</param>
/// <param name="encoder">URL encoder.</param>
/// <param name="clock">System clock.</param>
public AppServicesAuthenticationHandler(
IOptionsMonitor<AppServicesAuthenticationOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock)
: base(options, logger, encoder, clock)
{
}

// Constants
private const string AppServicesAuthIdTokenHeader = "X-MS-TOKEN-AAD-ID-TOKEN";
private const string AppServicesAuthIdpTokenHeader = "X-MS-CLIENT-PRINCIPAL-IDP";

/// <inheritdoc/>
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (AppServicesAuthenticationInformation.IsAppServicesAadAuthenticationEnabled)
{
string? idToken = GetIdToken();
string? idp = GetIdp();

if (idToken != null && idp != null)
{
JsonWebToken jsonWebToken = new JsonWebToken(idToken);
bool isAadV1Token = jsonWebToken.Claims
.Any(c => c.Type == Constants.Version && c.Value == Constants.V1);
ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(
jsonWebToken.Claims,
idp,
isAadV1Token ? Constants.NameClaim : Constants.PreferredUserName,
ClaimsIdentity.DefaultRoleClaimType));

AuthenticationTicket ticket = new AuthenticationTicket(claimsPrincipal, AppServicesAuthenticationDefaults.AuthenticationScheme);
AuthenticateResult success = AuthenticateResult.Success(ticket);
return Task<AuthenticateResult>.FromResult<AuthenticateResult>(success);
}
}

// Try another handler
return Task.FromResult(AuthenticateResult.NoResult());
}

private string? GetIdp()
{
string? idp = Context.Request.Headers[AppServicesAuthIdpTokenHeader];
#if DEBUG
if (string.IsNullOrEmpty(idp))
{
idp = AppServicesAuthenticationInformation.SimulateGetttingHeaderFromDebugEnvironmentVariable(AppServicesAuthIdpTokenHeader);
}
#endif
return idp;
}

private string? GetIdToken()
{
string? idToken = Context.Request.Headers[AppServicesAuthIdTokenHeader];
#if DEBUG
if (string.IsNullOrEmpty(idToken))
{
idToken = AppServicesAuthenticationInformation.SimulateGetttingHeaderFromDebugEnvironmentVariable(AppServicesAuthIdTokenHeader);
}
#endif
return idToken;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Linq;

namespace Microsoft.Identity.Web
{
/// <summary>
/// Information about the App Services configuration on the host.
/// </summary>
public static class AppServicesAuthenticationInformation
{
// Environment variables.
private const string AppServicesAuthEnabledEnvironmentVariable = "WEBSITE_AUTH_ENABLED"; // True
private const string AppServicesAuthOpenIdIssuerEnvironmentVariable = "WEBSITE_AUTH_OPENID_ISSUER"; // for instance https://sts.windows.net/<tenantId>/
private const string AppServicesAuthClientIdEnvironmentVariable = "WEBSITE_AUTH_CLIENT_ID"; // A GUID
private const string AppServicesAuthClientSecretEnvironmentVariable = "WEBSITE_AUTH_CLIENT_SECRET"; // A string
private const string AppServicesAuthLogoutPathEnvironmentVariable = "WEBSITE_AUTH_LOGOUT_PATH"; // /.auth/logout
private const string AppServicesAuthIdentityProviderEnvironmentVariable = "WEBSITE_AUTH_DEFAULT_PROVIDER"; // AzureActiveDirectory
private const string AppServicesAuthAzureActiveDirectory = "AzureActiveDirectory";

// Artificially added by Microsoft.Identity.Web to help debugging App Services. See the Debug controller of the test app
private const string AppServicesAuthDebugHeadersEnvironmentVariable = "APP_SERVICES_AUTH_LOCAL_DEBUG";

/// <summary>
/// Is App Services authentication enabled?.
/// </summary>
public static bool IsAppServicesAadAuthenticationEnabled
{
get
{
return (Environment.GetEnvironmentVariable(AppServicesAuthEnabledEnvironmentVariable) == Constants.True)
&& Environment.GetEnvironmentVariable(AppServicesAuthIdentityProviderEnvironmentVariable) == AppServicesAuthAzureActiveDirectory;
}
}

/// <summary>
/// Logout URL for App Services Auth web sites.
/// </summary>
public static string? LogoutUrl
{
get
{
return Environment.GetEnvironmentVariable(AppServicesAuthLogoutPathEnvironmentVariable);
}
}

/// <summary>
/// ClientID of the App Services Auth web site.
/// </summary>
internal static string? ClientId
{
get
{
return Environment.GetEnvironmentVariable(AppServicesAuthClientIdEnvironmentVariable);
}
}

/// <summary>
/// Client secret of the App Services Auth web site.
/// </summary>
internal static string? ClientSecret
{
get
{
return Environment.GetEnvironmentVariable(AppServicesAuthClientSecretEnvironmentVariable);
}
}

/// <summary>
/// Issuer of the App Services Auth web site.
/// </summary>
internal static string? Issuer
{
get
{
return Environment.GetEnvironmentVariable(AppServicesAuthOpenIdIssuerEnvironmentVariable);
}
}

#if DEBUG
/// <summary>
/// Get headers from environment to help debugging App Services authentication.
/// </summary>
internal static string? SimulateGetttingHeaderFromDebugEnvironmentVariable(string header)
{
string? headerPlusValue = Environment.GetEnvironmentVariable(AppServicesAuthDebugHeadersEnvironmentVariable)
?.Split(';')
?.FirstOrDefault(h => h.StartsWith(header));
return headerPlusValue?.Substring(header.Length + 1);
}
#endif
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Microsoft.AspNetCore.Authentication;

namespace Microsoft.Identity.Web
{
/// <summary>
/// Options for Azure App Services authentication.
/// </summary>
public class AppServicesAuthenticationOptions : AuthenticationSchemeOptions
{
}
}
Loading

0 comments on commit b9de3cc

Please sign in to comment.