Finding out the current Identity Provider in Windows Identity Foundation - c#

I have a web site which allows multiple ways of authenticating a user (e.g. facebook, twitter, windows etc.) Each of this provides a different identity name when the user logs in. I need to find out who provided the identity (ClaimsIdentity) and accordingly select the unique id for the user to add application specific claims to the users claims set.
I created a table to associate all the entities with users primary profile table. This table contains name of the identity provider, unique id provided by the identity provider and unique user id from the profile table.
My question is how can I find the name of the identity provider when the user signs into my site using these logins? The problem is if the user has same email address used for both facebook and twitter, I am not able to find out that information in the incoming principal as used in the authentication manager's authenticate method.

You typically use the Issuer and OriginalIssuer properties in each claim that you get.
If you use e-mail as the unique identifier:
var u = this.User as IClaimsPrincipal;
var c = (u.Identity as IClaimsIdentity)
.Claims
.First( c => c.ClaimType == ClaimTypes.Email );
var issuer = c.Issuer;
var originalIssuer = c.OriginalIssuer;

Related

How can I uniquely identify Active Directory guest users in a Blazor/ASP application

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?
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
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.

ASP.NET Boilerplate Allow Self-Provisioning Tenant Registration

so im trying to create a SaaS application with ASP.NET Boilerplate, and i come into some problem as follows:
As i observe the framework, i noted that the "RegisterAsync" function in UserRegistrationManager create user based on the currently active tenant. It means if i currently log in on tenant '1', then when i register new user, the new user will have tenantId '1'. On the other hand, when i currently not logged in, if i register a new user, the app will show exception 'cannot register host user'.
public async Task<User> RegisterAsync(string name, string surname, string emailAddress, string phoneNumber, string userName, string plainPassword, bool isEmailConfirmed)
{
CheckForTenant();
var tenant = await GetActiveTenantAsync();
var user = new User
{
TenantId = tenant.Id,
Name = name,
Surname = surname,
EmailAddress = emailAddress,
PhoneNumber = phoneNumber,
IsActive = true,
UserName = userName,
IsEmailConfirmed = isEmailConfirmed,
Roles = new List<UserRole>()
};
return user;
}
private void CheckForTenant()
{
if (!AbpSession.TenantId.HasValue)
{
throw new InvalidOperationException("Can not register host users!");
}
}
The application that i want to build requires the function for new user to be able to sign up along with free trial and then paid subscription. So i think that the new user should be able to create tenant by themself. So if the new user register, they will be forced to create new tenant before they can do any other thing in the app.
The problem is that the tenantId column in User table cannot be null, so i can register without tenant. Im thinking of assign all newly created user to 'Default' tenant at first, but i think that this was not the best practices.
Is there any way to overcome this problem or any references about that? Thanks in advance!
Based on my empirical SaaS Application development experience, a typical Self-Signup flow in Multi-Tenant applications would be like the one given below
User opts to self-signin
Allow the user to pick a subscription plan (most likely a trial plan)
Get the Company Name (tenant name) as part of the signup flow
Create a new tenant that has the subscription (2)
Add the signup user as the administrator for that tenant
In case of a trial plan, set up the suitable request handler to keep validating if the tenant has crossed the subscribed number of trial days, in that case, force redirect to payment page or signout
If the user has opted to Signup for a paid subscription (during signup), after provisioning the tenant go to the payment page. Once payment succeeds, capture the transactionid and allow the user to login and use the application.
The flow that you wanted to be using is straightforward
Build a custom self-signup process, obtain the company name (Tenant Name)
Also capture the emailid of the user that is performing the sign-up
Create the tenant based on info from (1)
Set the administrator for the tenant based on the info from (2)
All your API calls should be working fine.
Note
Have a separate Self-Signup Service like (TenantSelfRegistrationService) so that you can allow anonymous access to that service.
In terms of security, set captcha and set rate-limits or CSRF Tokens etc to enforce security in the signup process.
Hope this clarifies
I looked at the code and the documentation and I think you should never allow an unknown user to create new tenants. This should happen by a person who has the correct authorization to create tenants. This is a user that exists in the host tenant.
You as admin in the host tenant need to create tenant for somebody else and add them as admin for that tenant.
Registering users is then done through the normal way with the register webpage running for that tenant.
How to do that, I leave to you to figure out with the documentation of boilerplate itself! Documentation

How to integrate LDAP in WPF application that consumes WCF service

I will start by describing how my application works today without LDAP.
I have WPF application that consumes WCF services (authentication windows or UserName depends on users choice). This services allows communication with database.
I display to user a "Login screen" in order to allow him set her "user name" and "password" then application connects to service and consumes function that checks if UserName and Password exist in database. (see img below)
Now I need also to integrate LDAP for authenticating user accounts against their existing systems instead of having to create another login account.
I'm bit ignorant about LDAP and confused about many things. Please excuse the possible use of wrong terminology.
I googled but I still don't have answers of many questions.
1- What is the relation between users that exist in my database table "User" and profiles that I should be created in LDAP ?
2- What is the check I should do to allow user come from LDAP to access to my application and use all functionnalities of my service ?
3- Should I have service type "LDAP" like other authentications types I have today in my application ("Windows" and "UserName") ?
4- If I want to update my application architecture described in picture above where should I add LDAP ?
First I am going to answer your questions one by one,
The user on LDAP is the same on DB, you can hold LDAP's Username and it's domain in your Users Table,
but the profile on the LDAP may vary with your profile table, but it can be fetched from LDAP address.
It's enough to check username and password over LDAP, just need to hold LDAP addresses in a Table (example ExternalPath) and make a relation between User and ExternalPath tables. LDAP address is contains some specifications.
Yes, you have to have a separate mechanism for identifying LDAP Users which I will explain more further.
This is not hard if everything be atomic and designed right, in further steps you may see it is easy.
So let me tell about my experience in LDAP and Authenticate users on LDAP and DB and our architecture.
I was implemented a WCF service named Auth.svc, this service contains a method named AuthenticateAndAuthorizeUser this is transparent for user which came from LDAP or anywhere.
I hope you get the clue and architecture to Authenticate user over LDAP and DB in below steps:
1- First I have a table named Users which hold users info and one more field named ExternalPath as foreign key, if it is null specify UserName is in DB wit it's password otherwise it is came from UserDirectory.
2- In second step you have to hold LDAP address (in my case LDAP addresses are in ExternalPath table), all LDAP addresses are on port 389 commonly.
3- Implementing authenticate User, if is not found(with Username and Password) then check it's ExternalPath to verify over LDAP address.
4- The DB schema should be something like below screenshot.
As you can see ExternalPath field specify user is from LDAP or not.
5- In presentation layer I am defining LDAP servers like below screenshot also
6- In the other side while adding new user in system you can define LDAP for user in my case I am listing LDAP titles in a DropDown in adding User form (if admin select LDAP address then don't need to get password and save it in DB), as I mentioned just need to hold LDAP username not password.
7- But last thing is authenticating user on LDAP and DB.
So the authenticate method is something like:
User userLogin = User.Login<User>(username, password, ConnectionString, LogFile);
if (userLogin != null)
return InitiateToken(userLogin, sourceApp, sourceAddress, userIpAddress);
else//Check it's LDAP path
{
User user = new User(ConnectionString, LogFile).GetUser(username);
if (user != null && user.ExternalPath != null)
{
LDAPSpecification spec = new LDAPSpecification
{
UserName = username,
Password = password,
Path = user.ExternalPath.Path,
Domain = user.ExternalPath.Domain
};
bool isAthenticatedOnLDAP = LDAPAuthenticateUser(spec);
}
}
If userLogin does not exist in DB by entered UserName and Password then we should authenticate it over related LDAP address.
In else block find User from Users table and get it's ExternalPath if this field was not null means User is on LDAP.
8- The LDAPAuthenticateUser method is :
public bool LDAPAuthenticateUser(LDAPSpecification spec)
{
string pathDomain = string.Format("LDAP://{0}", spec.Path);
if (!string.IsNullOrEmpty(spec.Domain))
pathDomain += string.Format("/{0}", spec.Domain);
DirectoryEntry entry = new DirectoryEntry(pathDomain, spec.UserName, spec.Password, AuthenticationTypes.Secure);
try
{
//Bind to the native AdsObject to force authentication.
object obj = entry.NativeObject;
DirectorySearcher search = new DirectorySearcher(entry);
search.Filter = "(SAMAccountName=" + spec.UserName + ")";
search.PropertiesToLoad.Add("cn");
SearchResult result = search.FindOne();
if (null == result)
{
return false;
}
}
catch (Exception ex)
{
Logging.Log(LoggingMode.Error, "Error authenticating user on LDAP , PATH:{0} , UserName:{1}, EXP:{2}", pathDomain, spec.UserName, ex.ToString());
return false;
}
return true;
}
If exception raised in LDAPAuthenticateUser means User does not exist in User Directory.
The authentication code accepts a domain, a user name, a password, and a path to the tree in the Active Directory.
The above code uses the LDAP directory provider the authenticate method calls LDAPAuthenticateUser and passes in the credentials that are collected from the user. Then, a DirectoryEntry object is created with the path to the directory tree, the user name, and the password. The DirectoryEntry object tries to force the AdsObject binding by obtaining the NativeObject property. If this succeeds, the CN attribute for the user is obtained by creating a DirectorySearcher object and by filtering on the SAMAccountName. After the user is authenticated and exception not happened method returns true means user find on given LDAP address.
To see more info about Lightweight Directory Access Protocol and authenticate over it THIS Link can be useful which tells about specification more.
Hope will help you.

How to get user name, email, etc. from MobileServiceUser?

After a lot of digging around I've got my WPF application signing users in via Azure Mobile Service. My Mobile Service is connected to an Azure Active Directory that I have set up. However, when I log the user in with MobileServiceClient.LoginAsync(...) the MobileServiceUser UserId is in an unreadable hash it seems. For example it looks like: "Aad:X3pvh6mmo2AgTyHdCA3Hwn6uBy91rXXXXXXXXXX". What exactly is this?
I'd like to grab the user's display name to use but I can't figure out how.
That is the userID of Azure Active Directory. You need to create a service to expose your AAD info through a service and retrieve the additional information using the access token you get from your user.
First:
ServiceUser user = this.User as ServiceUser;
var identities = await user.GetIdentitiesAsync();
var aad = identities.OfType<AzureActiveDirectoryCredentials>().FirstOrDefault();
var aadAccessToken = aad.AccessToken;
var aadObjectId = aad.ObjectId;
This will give you the access token and objectID , then you need to query the information through AAD graphy API.
https://msdn.microsoft.com/library/azure/dn151678.aspx
Look at the sample request part. You should provide the query with the access token you got and objectId.
Here is an alternative approach, after reading http://justazure.com/azure-active-directory-part-2-building-web-applications-azure-ad/ scroll to the section on Identity in .Net it talks how claims are a standard part of the framework. So once you get the credentials object like provided by #beast
var aad = identities.OfType<AzureActiveDirectoryCredentials>().FirstOrDefault();
You can actually grab a dictionary with the various properties. Examples of some the properties can be found at https://msdn.microsoft.com/en-us/library/system.identitymodel.claims.claimtypes(v=vs.110).aspx
So from there you can do the following:
if (aad != null)
{
var d = aad.Claims;
var email = d[ClaimTypes.Email];
}
I did this to pull the user id which was cross referenced in a table. FYI I am using App Service, but I believe the credentials object is the same in Mobile Service

How to populate user(Identity) roles for a Web application when roles are stored in a SQL Server database

I have a C# based asp.net application which does a form based authentication and also needs authorization.
Here is the simplified version of the User table (SQL Server)
UID UName PasswordHash Userroles
----------------------------------------------
1 a GERGERGEGER Proivder;Data Entry
2 b WERGTWETWTW HelpDSK; UserNamager
...
...
I'm quite familiar with the Authentication part. But for Authorization I am not sure what is the best way:
I know once user is Authorized, you can use the Identity object to get his/her info.
The question is what my choice to read the logged in user's roles on every page other than call that DB table every time to get them?
I am not sure this is a SQL Server question. This is an ASP.NET question.
ASP.NET forms authentication allows the application to define a "Principal" which (among other things) contains an array of strings known as "roles." You can populate the roles from the DB one time (when the user signs on) then serialize the principal into the forms authentication ticket, which becomes an encrypted cookie on the browser. ASP.NET decodes the cookie with each http request and provides it to your ASP.NET c# code via HttpContext.User. It can then retrieve the roles from context and never needs to talk to the DB again.
Storing the roles would look something like this:
string roles = "Admin,Member";
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
1,
userId, //user id
DateTime.Now,
DateTime.Now.AddMinutes(20), // expiry
false, //do not remember
roles,
"/");
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName,
FormsAuthentication.Encrypt(authTicket));
Response.Cookies.Add(cookie);

Categories