1
0
mirror of https://github.com/bitwarden/server.git synced 2025-05-25 13:24:50 -05:00

identityserver v4 cleanup (#843)

* v4 cleanup

* idToken
This commit is contained in:
Kyle Spearrin 2020-07-31 09:45:36 -04:00 committed by GitHub
parent 623cd36bd4
commit 4df363aaa1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -84,7 +84,7 @@ namespace Bit.Identity.Controllers
throw new Exception("Organization not found."); throw new Exception("Organization not found.");
} }
var provider = "sso"; var scheme = "sso";
var props = new AuthenticationProperties var props = new AuthenticationProperties
{ {
RedirectUri = Url.Action(nameof(ExternalCallback)), RedirectUri = Url.Action(nameof(ExternalCallback)),
@ -92,11 +92,11 @@ namespace Bit.Identity.Controllers
{ {
{ "return_url", returnUrl }, { "return_url", returnUrl },
{ "domain_hint", domainHint }, { "domain_hint", domainHint },
{ "scheme", provider }, { "scheme", scheme },
}, },
}; };
return Challenge(props, provider); return Challenge(props, scheme);
} }
[HttpGet] [HttpGet]
@ -104,7 +104,7 @@ namespace Bit.Identity.Controllers
{ {
// Read external identity from the temporary cookie // Read external identity from the temporary cookie
var result = await HttpContext.AuthenticateAsync( var result = await HttpContext.AuthenticateAsync(
IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme); IdentityServerConstants.ExternalCookieAuthenticationScheme);
if (result?.Succeeded != true) if (result?.Succeeded != true)
{ {
throw new Exception("External authentication error"); throw new Exception("External authentication error");
@ -121,8 +121,8 @@ namespace Bit.Identity.Controllers
throw new Exception("Cannot find user."); throw new Exception("Cannot find user.");
} }
// this allows us to collect any additonal claims or properties // This allows us to collect any additional claims or properties
// for the specific prtotocols used and store them in the local auth cookie. // for the specific protocols used and store them in the local auth cookie.
// this is typically used to store data needed for signout from those protocols. // this is typically used to store data needed for signout from those protocols.
var additionalLocalClaims = new List<Claim>(); var additionalLocalClaims = new List<Claim>();
var localSignInProps = new AuthenticationProperties var localSignInProps = new AuthenticationProperties
@ -130,9 +130,9 @@ namespace Bit.Identity.Controllers
IsPersistent = true, IsPersistent = true,
ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(1) ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(1)
}; };
ProcessLoginCallbackForOidc(result, additionalLocalClaims, localSignInProps); ProcessLoginCallback(result, additionalLocalClaims, localSignInProps);
// issue authentication cookie for user // Issue authentication cookie for user
await HttpContext.SignInAsync(new IdentityServerUser(user.Id.ToString()) await HttpContext.SignInAsync(new IdentityServerUser(user.Id.ToString())
{ {
DisplayName = user.Email, DisplayName = user.Email,
@ -140,27 +140,29 @@ namespace Bit.Identity.Controllers
AdditionalClaims = additionalLocalClaims.ToArray() AdditionalClaims = additionalLocalClaims.ToArray()
}, localSignInProps); }, localSignInProps);
// delete temporary cookie used during external authentication // Delete temporary cookie used during external authentication
await HttpContext.SignOutAsync(IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme); await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);
// retrieve return URL // Retrieve return URL
var returnUrl = result.Properties.Items["return_url"] ?? "~/"; var returnUrl = result.Properties.Items["return_url"] ?? "~/";
var context = await _interaction.GetAuthorizationContextAsync(returnUrl); var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
if (context != null) if (context != null)
{ {
if (await IsPkceClientAsync(context.Client.ClientId)) if (IsNativeClient(context))
{ {
// if the client is PKCE then we assume it's native, so this change in how to // The client is native, so this change in how to
// return the response is for better UX for the end user. // return the response is for better UX for the end user.
HttpContext.Response.StatusCode = 200;
HttpContext.Response.Headers["Location"] = string.Empty;
return View("Redirect", new RedirectViewModel { RedirectUrl = returnUrl }); return View("Redirect", new RedirectViewModel { RedirectUrl = returnUrl });
} }
// we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null // We can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null
return Redirect(returnUrl); return Redirect(returnUrl);
} }
// request for a local page // Request for a local page
if (Url.IsLocalUrl(returnUrl)) if (Url.IsLocalUrl(returnUrl))
{ {
return Redirect(returnUrl); return Redirect(returnUrl);
@ -171,7 +173,7 @@ namespace Bit.Identity.Controllers
} }
else else
{ {
// user might have clicked on a malicious link - should be logged // User might have clicked on a malicious link - should be logged
throw new Exception("invalid return URL"); throw new Exception("invalid return URL");
} }
} }
@ -181,7 +183,7 @@ namespace Bit.Identity.Controllers
{ {
var externalUser = result.Principal; var externalUser = result.Principal;
// try to determine the unique id of the external user (issued by the provider) // Try to determine the unique id of the external user (issued by the provider)
// the most common claim type for that are the sub claim and the NameIdentifier // the most common claim type for that are the sub claim and the NameIdentifier
// depending on the external provider, some other claim type might be used // depending on the external provider, some other claim type might be used
var userIdClaim = externalUser.FindFirst(JwtClaimTypes.Subject) ?? var userIdClaim = externalUser.FindFirst(JwtClaimTypes.Subject) ??
@ -199,10 +201,10 @@ namespace Bit.Identity.Controllers
return (user, provider, providerUserId, claims); return (user, provider, providerUserId, claims);
} }
private void ProcessLoginCallbackForOidc(AuthenticateResult externalResult, private void ProcessLoginCallback(AuthenticateResult externalResult, List<Claim> localClaims,
List<Claim> localClaims, AuthenticationProperties localSignInProps) AuthenticationProperties localSignInProps)
{ {
// if the external system sent a session id claim, copy it over // If the external system sent a session id claim, copy it over
// so we can use it for single sign-out // so we can use it for single sign-out
var sid = externalResult.Principal.Claims.FirstOrDefault(x => x.Type == JwtClaimTypes.SessionId); var sid = externalResult.Principal.Claims.FirstOrDefault(x => x.Type == JwtClaimTypes.SessionId);
if (sid != null) if (sid != null)
@ -210,23 +212,19 @@ namespace Bit.Identity.Controllers
localClaims.Add(new Claim(JwtClaimTypes.SessionId, sid.Value)); localClaims.Add(new Claim(JwtClaimTypes.SessionId, sid.Value));
} }
// if the external provider issued an id_token, we'll keep it for signout // If the external provider issued an idToken, we'll keep it for signout
var id_token = externalResult.Properties.GetTokenValue("id_token"); var idToken = externalResult.Properties.GetTokenValue("id_token");
if (id_token != null) if (idToken != null)
{ {
localSignInProps.StoreTokens( localSignInProps.StoreTokens(
new[] { new AuthenticationToken { Name = "id_token", Value = id_token } }); new[] { new AuthenticationToken { Name = "id_token", Value = idToken } });
} }
} }
public async Task<bool> IsPkceClientAsync(string client_id) public bool IsNativeClient(IdentityServer4.Models.AuthorizationRequest context)
{ {
if (!string.IsNullOrWhiteSpace(client_id)) return !context.RedirectUri.StartsWith("https", StringComparison.Ordinal)
{ && !context.RedirectUri.StartsWith("http", StringComparison.Ordinal);
var client = await _clientStore.FindEnabledClientByIdAsync(client_id);
return client?.RequirePkce == true;
}
return false;
} }
} }
} }