0

I am using a Blazor application with Azure Active Directory authentication and guest users. When a user logs in, I want to use their authenticated identity as a key to lookup permissions and other attributes in my own database. Initially I used this code...

@using Microsoft.AspNetCore.Components.Authorization
@inject AuthenticationStateProvider AuthenticationStateProvider
@code {
   var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
   var user = authState.User;

   if (user.Identity.IsAuthenticated)
   {
        //Don't do this: Name != email and may not even be constant 
        var email = user.Identity.Name; 
        var attributes = await myDatabase.FetchAttributesForUser(email);
        //use attributes.... 
   }
}

However, user.Identity.Name is not the email address of the user. As described in this old post, it is simply an identifier that the authentication provider supplies. For example, a user with an outlook address of first.last@outlook.com might be authenticated with a Name of live#first.last@outlook.com There's therefore no guarantee that the Name may be unique across providers or even across time for the same provider.

This stackoverflow question is identifying the same problem and the accepted answer is to use the SID but what is missing is an explanation of how I can retrieve the SID from the AuthenticationStateProvider that's been injected into my application. (There are no obvious fields or paths that lead to a SID.) Should I be injecting some other authentication object?

Also, if the recommendation is to use the SID, how can I obtain it for pre-provisioning? I.e. I invite "jo.bloggs@contoso.com" and want to set up attributes in my database before she first logs in. Is the "object id" shown in the portal actually the SID?

agua from mars
  • 16,428
  • 4
  • 61
  • 70
NeilMacMullen
  • 3,339
  • 2
  • 20
  • 22
  • IMO its not recommended to use the SID (that can be a security issue) and I'm not sure you can get the SID on Azure AD. But you can probably ask for the mail claim. May be that can help : https://stackoverflow.com/questions/30983694/get-the-users-email-address-from-azure-ad-via-openid-connect – agua from mars Dec 06 '19 at 10:54
  • This doc to : https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-optional-claims Here they say SID is per session, so you cannot use it in your scenario – agua from mars Dec 06 '19 at 10:56
  • 1
    `upn` is what you should used I guess : `user.Claims.FirstOrDefault(c => c.Type == "upn")` – agua from mars Dec 06 '19 at 10:59
  • Thanks @aguafrommars - your examples were very useful. In fact the ClaimTypes enum should be used to compare against the Type since URIs are used. – NeilMacMullen Dec 06 '19 at 11:52
  • That depends the config and the auth method used, but, yeah, using an enum is better. – agua from mars Dec 06 '19 at 12:59

2 Answers2

1

In Azure AD you can use either the oid or sub claim. The oid claim contains the object id for the user in the Azure AD tenant they signed into. It is thus unique across applications. The sub claim is unique for that user in one application.

Both of them are immutable. A .NET ClaimsPrincipal may contain the oid claim with a different name: http://schemas.microsoft.com/identity/claims/objectidentifier.

One source for these alternate names is the System.Security.Claims.ClaimTypes class.

Claims in id tokens: https://learn.microsoft.com/en-us/azure/active-directory/develop/id-tokens#payload-claims

juunas
  • 54,244
  • 13
  • 113
  • 149
  • Thanks Juunas - will accept as answer once edits folded in. (Maybe there is a better way to retrieve oid than I could find?) – NeilMacMullen Dec 06 '19 at 11:50
  • I see that reviewers are rejecting the edit suggestion without understanding that it is a direct follow-on from your final paragraph. Since the link to the claims ids tokens talks about 'logical' tokens without making the relationship between URI and name clear, it's also helpful to know how to get to the URI needed to search the claims. (Since it took me 20 minutes to find this out myself it would be useful to future readers of this answer to have it laid out explicitly) – NeilMacMullen Dec 06 '19 at 15:47
  • You can also add that as a comment, I can then edit my answer – juunas Dec 06 '19 at 15:48
  • True - but I thought it was more 'friendly' to you to provide a preformatted addendum in the form of an edit. – NeilMacMullen Dec 06 '19 at 15:51
  • Anyway the "missing info" is that you need to use the ClaimTypes enum to look up most claims but there is no entry for 'oid' and you have to use a horrible bare URI instead. – NeilMacMullen Dec 06 '19 at 15:59
1

A general solution for obtaining the SID from the AuthenticationStateProvider with respect to the below in point asked in the original question.

This stackoverflow question is identifying the same problem and the accepted answer is to use the SID but what is missing is an explanation of how I can retrieve the SID from the AuthenticationStateProvider that's been injected into my application. (There are no obvious fields or paths that lead to a SID.) Should I be injecting some other authentication object?

If you notice, the AuthenticationState's User property returns the Identity property of type IIDentity which has the base functionalities for WindowsIdentity and other Identities as well. Hence converting the Identity into the WindowsIdentity becomes valid here. And after converting you can get the SID information of the user as below. The other information of the user can be obtained directly from the identity object.

var authenticationState = await authenticationStateTask;
WindowsIdentity identity = authenticationState.User.Identity as WindowsIdentity;

var SId = identity.User.Value;

Hope it helps.

Nagaraj .
  • 425
  • 1
  • 5
  • 13