Using Core Identity OAuth2 authentication (and retrieving user info) last MS samples uses such code:
options.Events = new OAuthEvents
{
// ...
OnCreatingTicket = async (OAuthCreatingTicketContext context) =>
{
var userInfo = // ..
context.RunClaimActions(userInfo);
}
}
MS call it "Action Claims".
But how to access those action claims later, in the controller code ?
To be concrete, how to access them in the "Core Identity 2.1" generated Razor pages user external login OnGetCallbackAsync
// ExternalLogin.cshtml.cs
public async Task<IActionResult> OnGetCallbackAsync(string returnUrl = null, string remoteError = null)
{
var claimsIdentity = User.Identity as ClaimsIdentity;
var claimsPrincipal = User.Identity as ClaimsPrincipal; // null
// claimsIdentity doesn't contains oauth target claims (since this new not registered yet user?)
// ..
var signInResult = await _signInManager.ExternalLoginSignInAsync(...);
if (signInResult.Succeeded)
{
}else // means yet not registered locally
{
// HOW TO ACCESS ACTION CLAIMS THERE?
// or how to get authentication token to get user info manually...
}
}
P.S.
Additionally to the asnwer: RunClaimActions
should be used together with MapJsonKey
serviceCollection.AddAuthentication().AddOAuth(options =>
{
// ...
// https://msdn.microsoft.com/en-us/library/microsoft.identitymodel.claims.claimtypes_members.aspx
options.ClaimActions.MapJsonKey(ClaimTypes.Surname, "family_name");
options.ClaimActions.MapJsonKey(ClaimTypes.GivenName, "given_name");
options.ClaimActions.MapJsonKey("SalesforceOrganizationId", "organization_id");
Then userinfo fields can be accessed as regular user claims. Therefore "Action claims" are not "special type of claims" but just "yet another ASP MVC magic".
Also do not forget about options.SaveTokens = true;
Only with it you will be able to get token as
var info = await _signInManager.GetExternalLoginInfoAsync();
var token = info.AuthenticationTokens ()[0];
and get more information from other connected services.
I've seen this example in official documentation.
Reference Persist additional claims and tokens from external providers in ASP.NET Core
You would first have to map the desired claims when configuring the authentication provider
The documentation example used Google's, where it map user data keys and create claims
In the provider's options, specify a
MapJsonKey
for each key in the external provider's JSON user data for the app identity to read on sign in.
Startup
services.AddAuthentication().AddGoogle(options => {
//....
options.ClaimActions.MapJsonKey(ClaimTypes.Gender, "gender");
//...map other claims/claim types
//...
}
And from there you should be able to access claims via the ExternalLoginInfo.Principal
which has the ClaimsPrincipal
representing the user associated with the login.
ExternalLogin.cshtml.cs
//Executes when a previously registered user signs into the app.
public async Task<IActionResult> OnGetCallbackAsync(
string returnUrl = null, string remoteError = null) {
if (remoteError != null) {
ErrorMessage = $"Error from external provider: {remoteError}";
return RedirectToPage("./Login");
}
// Get the information about the user from the external login provider
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null) {
return RedirectToPage("./Login");
}
// Sign in the user with this external login provider if the user
// already has a login
var result = await _signInManager.ExternalLoginSignInAsync(
info.LoginProvider, info.ProviderKey, isPersistent: false,
bypassTwoFactor : true);
if (result.Succeeded) {
// Store the access token and resign in so the token is included in
// in the cookie
var user = await _userManager.FindByLoginAsync(info.LoginProvider,
info.ProviderKey);
// What is the gender of this user if present
if (info.Principal.HasClaim(c => c.Type == ClaimTypes.Gender)) {
var gender = info.Principal.FindFirst(ClaimTypes.Gender);
//...use gender
}
var props = new AuthenticationProperties();
props.StoreTokens(info.AuthenticationTokens);
await _signInManager.SignInAsync(user, props, info.LoginProvider);
_logger.LogInformation(
"{Name} logged in with {LoginProvider} provider.",
info.Principal.Identity.Name, info.LoginProvider);
return LocalRedirect(Url.GetLocalUrl(returnUrl));
}
if (result.IsLockedOut) {
return RedirectToPage("./Lockout");
} else {
// If the user does not have an account, then ask the user to
// create an account
ReturnUrl = returnUrl;
LoginProvider = info.LoginProvider;
if (info.Principal.HasClaim(c => c.Type == ClaimTypes.Email)) {
Input = new InputModel {
Email = info.Principal.FindFirstValue(ClaimTypes.Email)
};
}
return Page();
}
}
Review the comments in the code and note the access to info.Principal.Identity
, which should contain the claims associated with the current user after resigning in.