I'm looking for the best practice way of saving user identification data in my MVC application. Users authenticate onto the site using Azure AD. When a user saves data I want the "user id" to be saved on that record to uniquely identify it and also allow users to return their own set of data.
What is the best method to get the users AD unique identifier?
Azure AD provides claim based authentication. You can retrieve authenticated user identifier from current ClaimsPrincipal object.
We use the objectidentifier or oid as mentioned on the page I've linked below.
The sub claim might also be the right choice, depending on the need of your application(s), but it doesn't seem to be included anymore by default.
Optionally, tid in combination with oid might also be useful if you need to share data across services.
https://learn.microsoft.com/en-us/azure/active-directory/develop/id-tokens
And more specifically:
https://learn.microsoft.com/en-us/azure/active-directory/develop/id-tokens#using-claims-to-reliably-identify-a-user-subject-and-object-id
Related
I'm having some issues with getting the currently signed-in user's username from Azure b2c to my Blazor web app. Essentially, I want to make a username account where a username is the User Principal Name. I was originally using an email sign-up policy with a display name as the username. However, b2c doesn't enforce display names to be unique, so there's an inherent issue with that. No claim comes back with a username in it, so I have no way in my Blazor app to keep tabs on who is signed in or display their username. There are a few options I can do, and I'm curious which one is the easiest to implement/how to implement it, cause I've been ripping my hair out with this the past few hours.
On b2c account creation, take User Principal Name and also make it the display name
Have the user principal name be sent via claims (Not sure how to do this)
Do Email sign up with Display Names, but somehow enforce b2c to ensure display names are unique
Some other way to have a unique display name for users from b2c
Thanks in advance!
You need to use custom policies: https://aka.ms/ief. Username is not returned in the token with User Flows.
Using custom policies, you can sign up the user using username, and return their username (signInNames.username attribute, shown as userPrincipalName in the Azure Portal) into the token.
You can quick deploy custom policies using my tool here. And quick deploy the username sample using this page with the sample folder name username-signup-or-signin.
If you need any additional claim you need to enter in your Azure AD BC tenant and configure the Application Claims from your User Flow, in the following example the User flow is called B2C_1_signupsignin:
The above example, as you can see, lists all the other available claims, because dome claims are always returned, as the User Name.
You can configure the claim (mode) used to login in the Identity Provider:
Than the claims you receive are the following:
But as you can see there isn't any userPrincipalName or signinNames.
This is a known issue in Azure AD B2C and including this claims from the user flow manifest doesn't work.
The only solution I've found is to upload your custom policy and add a:
<OutputClaim ClaimTypeReferenceId = "signInName" />
but this works only for local accounts.
If you want to add signin names also for external (social) accounts you need to map them, like:
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="upn" />
look at the base source XML here:
https://github.com/azure-ad-b2c/samples/tree/master/policies/sign-up-with-social-and-local-account
I'm working on a web application based on ASP.NET Core MVC 2.1. It provides the ability to execute several Active Directory related operations. One of them is a web based LAPS client. To communicate with AD, I'm using System.DirectoryServices from Microsoft.Windows.Compatibility.
Since LAPS stores its data in a computer objects AD attributes (ms-Mcs-AdmPwd), I need to query this attribute, e.g. like this:
using (PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, targetDomain)) {
ComputerPrincipal computer = ComputerPrincipal.FindByIdentity(principalContext, IdentityType.Name, computerName);
string password = (computer.GetUnderlyingObject() as DirectoryEntry).Properties["ms-Mcs-AdmPwd"].Value.ToString();
}
So my problem is: I need to do this in context of the authenticated user, because the attribute security permissions already control access to the LAPS passwords. I implemented cookie authentication without Identity querying PrincipalContext.ValidateCredentials() against AD in order to authenticate users. What would be the best way to achieve this without asking the user for login data a second time?
There is a PrincipalContext constructor PrincipalContext(ContextType contextType, string name, string userName, string password), but this would require a way to store the password for each session. Storing the password in the session itself would be a bad idea, I guess.
Furthermore, I could just query the data on server side as ApplicationPoolIdentity or as a dedicated service account (which would then need full access to the relevant attributes of all computer objects) and then implement some logic to determine, if the logged in user is permitted to access this specific password. But this would result in unneccessary effort, since all authorization information already exist as AD DACLs.
I hope this makes sense somehow. So if anyone can push me in the right direction, I would be very grateful. Thanks in advance!
After all, I went with a mapping approach in order to determine, if a user is allowed to access data. If the user is member of a certain AD group containing the admins of a certain department and the target computer object is located in the corresponding OU, the password will be queried and subsequently displayed. ApplicationPoolIdentity executes the actual AD query. Works flawlessly so far.
I'm currently designing a system using asp.net core and I'd like to implement claims based authorization, but one particular part is confusing me.
When a claim is made the claim will include the type and the value and optionally an issuer. In a handler this claim and the issuer may be checked before access is confirmed.
However this issuer is not stored in the Identity db, so how does the handler then check the issuer?
Am I misunderstanding how this all works? My understanding was that a user makes a claim of some type, that their claim is of a certain value and the issuer is the validator of the claim type actually having that value for that user.
The handler will check the value and may check the issuer but it can't when the db does not store it. I don't understand the point of the issuer then.
I'd like the user to have a collection of claims, including who/what verifies those claims and for the application to at any time be able to verify those claims.
Please help me understand.
I have tested this as so:
Using a asp.net core app with Identity.
Register a user.
Add a claim to a user that includes a type, a value and an issuer. (for example, EmployeeNumber, 312, Microsoft.
Add an [Authorize(Policy="MicrosoftEmployeesOnly")] on a controller/action to restrict access.
Add the policy into services in StartUp.cs with a requirement.
Add requirement code that has a handler that checks the user has a claim of type EmployeeNumber, has a value and it is issued by Microsoft.
Login and the users claims will have been loaded in from the db into the identity.
The handler will fail to validate the user because the issuer (Microsoft) has been lost and now just says Local Authority.
The only thing I can think of here, is once the claim is added in to the db, it is considered validated by Microsoft and now held by the app (Local Authority) on behalf of Microsoft.
If that's true then:
Why check the issuer at all in any handler?
How do you revoke a claim?
I would prefer to be able to optionally go to that issuer and check the claim whenever I want, meaning the issuer could revoke/invalidate the claim. The employee makes the claim they have an employee number at Microsoft and initially Microsoft validate that. Some time later, Microsoft kick the employee out and on their system remove him. The app should be able to check with Microsoft each time the user logs in to see if the claim is valid. In this case it would not be valid any more.
Am I going slightly mad?
Posting this here as you linked to this question from my blog, and it may be useful to someone
I think you have misunderstood slightly about the nature of a claim,
which I can understand given the terminology. You seem to be taking
'Claim' as meaning the user is 'professing' that they have a certain
attribute, and you want to check that this is true.
That is not the way claims work here. Claims are essentially
'attributes' of the user. In the old way of working with roles, a user
would belong to a certain number of roles. These are just 'attributes'
the user has now, so are more generic. A user may have a number of
claims corresponding to the roles they are in.
The identity of the user is checked during authentication, and at that
point you assign the set of Claims that the user has to the
ClaimsIdentity object. This is the point you fetch the claims from
the database, and make sure they only get the ones they should have.
If you need to have someone verifying claims, then you would need to
have that whole process happening outside of this. Only the claims
which have been confirmed should be added to the ClaimsIdentity.
Now, there is an event you can handle on the
CookieAuthenticationMiddleware to validate a security ticket when it
is loaded on subsequent requests called ValidatePrincipal, but I'm not
sure if this is actually what you need.
And your subsequent response:
Thank you for your response. I understand now that these claims are
effectively verified claims once they get into the db. I guess they
could be removed from the db as a way of revoking the claim.
However, I think, as you suggest, the best way is to have this system
outside and it just provides claims as and when required. The design
is that the application will have accounts for different types of
entity and accounts will be able to make claims, for example that "I
am a parent". The parent would seek an authorizing account to validate
this. This might require the authorizing account holder to actually
see some real documentation before validating. Other claims, could
change. For example a parent with Parental Responsibility would need a
bit more verification, but may also lose that Parental Responsibility
in the real world and so a facility for revoking the claim needs to be
available.
So, I think the design should be to use the claims system with the
Authorize attribute following your excellent articles, but have a
separate system that allows for validation and revoking that feeds
that claims system.
I'm building a sign-up / login flow for a web site. I plan to use Facebook as my identity provider instead of rolling my own.
I have a good feel for the server-side login flow with Facebook:
Call FB login API to get a code
Exchange the code for a user access token
Inspect the user access token
Validate the user access token details
After these steps, I'd like to check if the authenticated user is already registered in my system. If yes, simply return a newly generated bearer token, so the user can make subsequent requests to resource servers to access protected data.
If the user is not registered in the system, however, then I'd like to register them by creating a database entry. Before creating this entry though, I'd like to collect one piece of information from the user. Namely, I'd like for them to tell me their desired 'username'. I will use this unique username as my database primary key.
I'm not 100% sure on how to securely ask the user for their desired username before creating the database entry. This is my question. :)
One thought I had was to create a "redemption code". This code would be encrypted and contain the user initialization details, a secret only the server would know, and a timestamp. Something like this:
code: {
mySecret: "super-secret-value",
expirationDate: "date-value",
user: { ... },
}
After seeing the user is not in my system, I'd respond with the code + redirect the client to a page where they'd be able to specify their username. Upon submitting their username + code back up to the server, I could decrypt the code, and validate mySecret to determine the code is not tampered. If all is good, create the user in the database with the user information from the redeemed code. Lastly, I'd generate a new bearer token for the user and send it to the client.
Questions
Is my proposed redemption code strategy a secure way of requesting a username before creating the backend DB entry?
If not, what would be?
If yes, what is a secure encryption/decryption routine to use for this purpose in C#?
Flow Sequence
Steps 1-4 from above correspond to "Login" through "Validate" arrows.
My proposed redemption code strategy corresponds to the purple arrows.
Red text corresponds to Facebook specific nomenclature.
Note, Stack Overflow does something very similar to what I want to do. Before creating your account on SO, it will ask you for your desired Display Name (this happens after authenticating via Facebook, Google, etc.). After submitting your display name, your account is registered.
Use open source IdentityServer3.
Whatever flow you choose its already standardized in their server. Including (if you want or need) OpenID, OAuth2 etc.
My asp.net mvc site needs some kind of authorization but it is rather different than the usual concept of users and thus membership.
It will be used more for preferences then for authentication actually. Accounts without any password should be possible (and will initially be the only type) and an account can also be used by multiple users at once. Depending on the user group it could be for example that all users of a certain region get a shared account.
This is a decision from the client('s marketing division) and is not up for discussion.
A certain landing page takes (only) a userId in the url that will load up an account which in turn has some preferences linked to it that can be used throughout the rest of the site.
If a user doesn't start at the landing page or the sent accountId doesn't match a record in the system, he/she will be assigned the default account that has default preferences.
I was thinking of not re-inventing the wheel (somebody should find a new expression for this) and use the asp.net Membership system.
But the whole system is based around required passwords, email and single sessions per user, which are all things I can't provide.
Because the situation is a bit unconventional I thought a custom MembershipProvider etc would be in place. But it seems the gist of this is inheriting from the regular Membership classes. The methods of these classes all require things I am not needing.
Any suggestions
You could use the standard Membership provider and using the Built in .Validate() method sending the Username and a Password that is "standard" for all accounts without authentication.
Have 2 different User Controls 1 for "Validated Login with Password" and one for "Share Account without password", each uses Membership-login but the latter needs to have a bit set on the field of the member that says "Public Account = True / 1 "
Good luck, seems like a fun project, would be cool to see the outcome ;)
By the way, you don't need to share the session, or you could, just stored the session in the database and map the session to a user instead of a cookie, might work?
As requested i'll elaborate on different user controls. Briefly i would have 2 Controls, one maybe called GlobalLogin and one called UserLogin, where GlobalLogin displays a Form which only has the Username, when submitted this will trigger a function that uses, as i stated before, a function which calls the Validate method in the Membership provider, with a pre-set password.
As a reflection, see all "Not logged in with password"-users as anonymous and treat them the same way, the only thing that is different is that they can access user-specific areas. This control also needs to check that a certain field in the database is set, such as a "Allows Globally Used Account Without Password"-field, where in this case, the bit / boolean needs to be true for this login to be accepted.
Now to the other part, the Control which handles Password Protected Accounts, this requires both Username & Password and this calls the Validate with these settings. Now, remember that when logged in with password, you can change your password, this SHOULD NOT be possible with a Global Account, because then your global password wouldnt work :)
There is detailed information on the Membership Provider at http://msdn.microsoft.com/en-us/library/f1kyba5e.aspx. Basically you need to create new provider, or derive from the existing, and overload the ValidateUser method to always return true.