I have the following setup:
- Application A:
- runs .net framework 4.6.1
- serves Angular application and API
- authenticate users using cookies
- authentification is done using OpenIdConnect and Active Directory (see implementation of
Startup.csbelow)
- Application B:
- runs on .net 6.0
- serves a SignalR endpoint
I would like application B usage to be restricted only to users authenticated on app A (as app A is the only way to use app B). From what I understood, the recommended way of doing this is to use a JWT token in application B. However, I don't know how to generate this JWT token so that users don't have to authenticate again.
So far, I have tried to authenticate users in app B using the cookies created in app A that are to app B (since they are on the same domain name) but this does not work (as I guess that the server does not have the session state).
I also tried to get the access_token using
var token = Request.GetOwinContext().Authentication.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationType).Properties.Dictionary["access_token"];
But the key does not exist in the dictionary.
Any help or hint would be greatly appreciated. Thank you !
/// <summary>
/// Startup class
/// </summary>
public partial class Startup
{
#region OpenIDConnect settings
// The Client ID is used by the application to uniquely identify itself to Azure AD.
string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
// RedirectUri is the URL where the user will be redirected to after they sign in.
string redirectUri = ConfigurationManager.AppSettings["ida:RedirectUri"];
// Tenant is the tenant ID (e.g. contoso.onmicrosoft.com, or 'common' for multi-tenant)
static string tenant = ConfigurationManager.AppSettings["ida:Tenant"];
// Authority is the URL for authority, composed by Microsoft identity platform endpoint and the tenant name
string authority = string.Format(CultureInfo.InvariantCulture, ConfigurationManager.AppSettings["ida:Authority"], tenant);
#endregion
#region Cookies Authentication settings
string cookieName = ConfigurationManager.AppSettings["cookies:Name"];
string cookieNameLegacy = ConfigurationManager.AppSettings["cookies:NameLegacy"];
string cookieDomain = ConfigurationManager.AppSettings["cookies:Domain"];
string logoutPath = ConfigurationManager.AppSettings["cookies:LogoutPath"];
#endregion
/// <summary>
/// OAuth authorization server options
/// </summary>
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
/// <summary>
/// Public Client Id
/// </summary>
public static string PublicClientId { get; private set; }
/// <summary>
/// Configure OWIN to use OAuth and OpenIdConnect
/// </summary>
/// <param name="app"></param>
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
var cookieAuthenticationOptions = new CookieAuthenticationOptions
{
CookieName = cookieName,
SlidingExpiration = true,
CookieDomain = cookieDomain,
LogoutPath = new PathString(logoutPath)
};
app.UseCookieAuthentication(cookieAuthenticationOptions);
var legacyCookieOptions = new LegacyCookiesOptions
{
CookieName = cookieNameLegacy
};
app.UseLegacyCookieAuthentication(legacyCookieOptions);
var openIdConnectAuthenticationOptions = new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
UseTokenLifetime = true, /*False in production*/
SaveTokens = true,
RedeemCode = true,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed,
SecurityTokenValidated = OnSecurityTokenValidated,
RedirectToIdentityProvider = OnRedirectToIdentityProvider
}
};
app.UseOpenIdConnectAuthentication(openIdConnectAuthenticationOptions);
app.UseClaimsTransformation(claimsPrincipal =>
{
// either add claims to incoming, or create new principal
var claims = new ClaimsPrincipal(claimsPrincipal);
AuthHelper.UpdateClaimName(claimsPrincipal.Identities.First());
return Task.FromResult(claims);
});
// This makes any middleware defined above this line run before the Authorization rule is applied in web.config
app.UseStageMarker(PipelineStage.Authenticate);
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/api/auth/token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
}
private Task OnRedirectToIdentityProvider(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
{
context.ProtocolMessage.DomainHint = "medpace.com";
string url = context.Request.Uri.GetLeftPart(UriPartial.Path);
bool IsSkipLogin = url.Contains("ForceAuthenticate.aspx") && context.OwinContext.Authentication.User.Identity.IsAuthenticated;
if (IsSkipLogin == false)
{
context.ProtocolMessage.Prompt = "login";
}
return Task.FromResult(0);
}
/// <summary>
/// Records the failed logon attempt
/// </summary>
/// <param name="context">Authentication failed notification context</param>
/// <returns></returns>
private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
{
var claims = context?.OwinContext?.Authentication?.User?.Identity as ClaimsIdentity;
var accountId = claims?.Name ?? string.Empty;
var remoteIpAddress = AuthHelper.GetRemoteIpAddress(claims);
//AddLogonAttempt(accountId, remoteIpAddress);
if (context.Exception is OpenIdConnectProtocolInvalidNonceException)
{
if (context.Exception.Message.Contains("IDX10311"))
{
context.SkipToNextMiddleware();
}
}
return Task.FromResult(0);
}
/// <summary>
/// Adds claims and record the successful logon attempt
/// </summary>
/// <param name="context">Security token validated notification</param>
/// <returns></returns>
private Task OnSecurityTokenValidated(SecurityTokenValidatedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
{
context.AuthenticationTicket.Properties.AllowRefresh = true;
var idToken = context.ProtocolMessage.IdToken;
var claims = context.AuthenticationTicket.Identity;
var accountId = AuthHelper.UpdateClaimsIdentity(claims, idToken);
var remoteIpAddress = AuthHelper.GetRemoteIpAddress(claims);
return Task.FromResult(0);
}