User.Identity.Name is different before and after publishing to Azure - asp.net-mvc

I am using #User.Identity.Name in my Asp.net mvc web application. This is shown as Domain\user on local and user#domain.com on azure site once deployed. Instead of using #User.Identity.Name, is there any way to make it unique on local and server. I have many code breaking areas due to this conflict.
I added Azure active directory authentication to my application which is hosted on Azure.
using (var context = new PrincipalContext(ContextType.Domain))
{
var principal = UserPrincipal.FindByIdentity(context, User.Identity.Name);
var displayName = principal?.DisplayName;
}
this displayName is shown on local and once published to Azure it is like null "". What would be the best way to show display name on Azure server once published?

It seems you are using the AD libraries for traditional on-premise AD. To program against Azure AD , you could use OpenID Connect to authenticate users from an Azure AD tenant. Please see the code sample below :
https://github.com/Azure-Samples/active-directory-dotnet-webapp-openidconnect
To get the display name of the current user , you could try :
var displayName = ((System.Security.Claims.ClaimsIdentity)User.Identity).Claims.Where(c => c.Type == "name").FirstOrDefault().Value;
If you also want to show Domain\DisplayName when using Azure AD , you could get domain name by :
var domian= ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn).Value.Split('#')[1].ToString();

Related

How to get User Identity object from Microsoft Graph with Azure AD B2C

I have successfully setup an Asp.Net Core web app using Azure AD B2C authentication and Microsoft Graph. I want to return the objectIdentity for the user so I can extract the username from the issuerAssignedId property. However the identities object is null so how can I get this value?
Here is my configuration in Startup.
services.AddMicrosoftIdentityWebAppAuthentication(Configuration, "AzureAdB2C");
services.Configure<OpenIdConnectOptions>(Configuration.GetSection("AzureAdB2C"));
services.AddSingleton<IGraphServiceClient>(implementationFactory =>
{
var clientId = Configuration["MicrosoftGraph:ClientId"];
var tenantId = Configuration["MicrosoftGraph:TenantId"];
var secret = Configuration["MicrosoftGraph:ClientSecret"];
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantId)
.WithClientSecret(secret)
.Build();
ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);
return new GraphServiceClient(authProvider);
});
Here is my API settings in Azure AD B2C.
Here is my test user account in Azure AD B2C. Because it is a local account with username I am trying to get the value under the User Principal Name highlighted below.
--- UPDATE ---
It is definitely a local account I created manually in Azure AD B2C. Here are the settings.
Installed Packages
It's because you use the Microsoft Graph v1.0 package.
In this case you need to use $select to get the identities property.
var user = await graphClient.Users[oid].Request().Select("identities").GetAsync();
Example here for your reference.
Or you can use Beta version (as #JasSuri suggested) in your project with Install-Package Microsoft.Graph.Beta -PreRelease and keep using your original code.

Get Customer Resource Id with Multi Tenant OAuth on Microsoft Dynamics

I want our my users who are using online Microsoft dynamics to give us access to their account through OAuth2. To be clear, my users live in their own tenants and NOT part of my tenant.
So I registered an Azure AD application and made it multi-tenant but the authorize URL required the resource id which is the exact customer URL on MS Dynamics. like
https://{orgid}.crm.dynamics.com
but I do not want the user to enter their URL manually. I want to automatically figure out their resource id during the OAuth process and complete the process.
how can I do so?
Btw, I am not using C# and I would appreciate it if the HTTP calls could be provided.
Recently I have been working/wrestling with some multi-tenant web apps that need to access D365 via OAuth2. While I do not claim to have mastered it, I have gotten a few such apps working.
Here are some thoughts for you:
I have not attempted to put the tenantId into the CRM url. To construct the CRM url I use the org "url name", which you can get using the below code.
This code assumes that you've put the base URL into the appSettings node of the web.config:
<add key="ida:OrganizationHostName" value="https://{0}.crm.dynamics.com" />
Then this code might should help you along:
private string getOrgName(string user)
{
var a = user.IndexOf('#');
var b = user.IndexOf('.');
return user.Substring(a + 1, b - (a + 1));
}
var signedInUserID = ClaimsPrincipal.Current.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier).Value;
var tenantID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
var userObjectID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
var OrganizationHostName = ConfigurationManager.AppSettings["ida:OrganizationHostName"];
var organizationName = getOrgName(User.Identity.Name);
var resource = string.Format(OrganizationHostName, organizationName);
Where I have seen the tenant Id used is when establishing the auth context
var authContext = new AuthenticationContext($"https://login.windows.net/{tenantID}");
As shown above you can get it from the ClaimsPrincipal, but I think my apps are all now using https://login.windows.net/common rather than appending the tenantId.
If you want to get fancy you can query the Discovery service to see what orgs a user has access to. For me, Colin Vermander's article was the key to getting that working.

To retrieve access token

I have created a MVC application to escalate work to other person inside my organization. I have added all the members in my organization to AAD,
and registered an application there, created app service and linked that app service to registered app with SSO enabled.
Now every time someone visits the app, they can login successfully using their respective credential.
What I want to do know is to retrieve all the members in my AAD and display them inside dropdown list so that anyone can escalate to others by just looking in the dropdown list.
I have tried with sample graph SDK to get the name of users in my organization
with this code
private string redirectUri = ConfigurationManager.AppSettings["ida:RedirectUri"];
private string appId = ConfigurationManager.AppSettings["ida:AppId"];
private string appSecret = ConfigurationManager.AppSettings["ida:AppSecret"];
private string scopes = ConfigurationManager.AppSettings["ida:GraphScopes"];
public async Task<string> GetUserAccessTokenAsync()
{
string signedInUserID = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
HttpContextWrapper httpContext = new HttpContextWrapper(HttpContext.Current);
TokenCache userTokenCache = new SessionTokenCache(signedInUserID, httpContext).GetMsalCacheInstance();
//var cachedItems = tokenCache.ReadItems(appId); // see what's in the cache
ConfidentialClientApplication cca = new ConfidentialClientApplication(
appId,
redirectUri,
new ClientCredential(appSecret),
userTokenCache,
null);
try
{
AuthenticationResult result = await cca.AcquireTokenSilentAsync(scopes.Split(new char[] { ' ' }), cca.Users.First());
return result.AccessToken;
}
// Unable to retrieve the access token silently.
catch (Exception)
{
HttpContext.Current.Request.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties() { RedirectUri = "/" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
throw new ServiceException(
new Error
{
Code = GraphErrorCode.AuthenticationFailure.ToString(),
Message = Resource.Error_AuthChallengeNeeded,
});
}
}
with some change in scope.
<add key="ida:AppId" value="xxxxx-xxxxxxx-xxxxxx-xxxxxxx"/>
<add key="ida:AppSecret" value="xxxxxxxxxxx"/>
<add key="ida:RedirectUri" value="http://localhost:55065/"/>
<add key="ida:GraphScopes" value="User.ReadBasic.All User.Read Mail.Send Files.ReadWrite"/>
This enables me to get basic details of all user in my organization.
But how I can achieve this in my app where authentication related stuffs are done in azure only, and there is no code for authentication and authorization in entire solution.
Thanks
Subham, NATHCORP, INDIA
But how I can achieve this in my app where authentication related stuffs are done in azure only, and there is no code for authentication and authorization in entire solution.
Based on my understanding, you are using the build-in feature App Service Authentication / Authorization. You could follow here to configure your web app to use AAD login. And you need to configure the required permissions for your AD app as follows:
Note: For Azure AD graph, you need to set the relevant permissions for the Windows Azure Active Directory API. For Microsoft Graph, you need to configure the Microsoft Graph API.
Then, you need to configure additional settings for your web app. You could access https://resources.azure.com/, choose your web app and update App Service Auth Configuration as follows:
Note: For using Microsoft Graph API, you need to set the resource to https://graph.microsoft.com. Details, you could follow here.
For retrieving the access token in your application, you could get it from the request header X-MS-TOKEN-AAD-ACCESS-TOKEN. Details, you could follow Working with user identities in your application.
Moreover, you could use Microsoft.Azure.ActiveDirectory.GraphClient package for Microsoft Azure Active Directory Graph API, Microsoft.Graph package for Microsoft Graph API using the related access token.

IdentityServer3 - redirect to ADFS if client is on intranet

We have a portal (mvc rdp) that is used by both internal users (employees) and external users (customers). We would like IdentityServer3 to automatically detect if the authentication request is done from within the corporate network, and redirect to ADFS. The local login should be shown if the user-agent is calling from the internet.
In short, we don't want to have buttons for the external idp as we want the IdSrv to automatically redirect to ADFS if client is on the internal network to provide true single sign on for our domain bound users.
If the portal was only used by internal users, then we would just configure the client to only use a particular identity provider but this portal is also used by external customers and those users are not stored in our AD ;)
I've looked at overriding PreAuthenticateAsync and using Dns.Dns.GetHostName() but that is related to the machine that IdentityServer is running on and not the client machine.
In an mvc controller, we would just use Request.UserHostName but this is not available in IdentityServer3 UserService.
I think you can get the client's IP address from the OwinContext; something like this:
public class UserService : UserServiceBase
{
OwinContext ctx;
public UserService(OwinEnvironmentService owinEnv)
{
ctx = new OwinContext(owinEnv.Environment);
}
public override Task PreAuthenticateAsync(PreAuthenticationContext context)
{
// The IP Address of the remote client
var ipAddress = ctx.Environment["server.RemoteIpAddress"].ToString();
if (BelongsToOurNetwork(ipAddress))
context.SignInMessage.IdP = "OurADFS";
else
context.SignInMessage.IdP = "idsrv"; // local login
return Task.FromResult(0);
}
}

Use synced Active Directory in Azure to validate users including groups?

I am porting an application to azure and in that app we use Active Directory to authenticate users like the following:
var user = model.UserName.Split('\\');
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, user[0]))
{
if (pc.ValidateCredentials(user[1], model.Password, ContextOptions.Negotiate))
{
using (var adUser = UserPrincipal.FindByIdentity(pc, IdentityType.SamAccountName, user[1]))
{
if (!MembershipService.ValidateUser(model.UserName, model.Password))
{
using (var userDb = new UsersDbContext())
{
if (userDb.aspnet_Users.Count(u => u.UserName.ToLower().Contains(model.UserName)) <= 0)
MembershipService.CreateUser(model.UserName, model.Password, adUser.EmailAddress);
else
{
var msUser = Membership.GetUser(model.UserName);
msUser.ChangePassword(msUser.ResetPassword(), model.Password);
}
}
}
FormsService.SignIn(model.UserName, model.RememberMe);
foreach (var role in Roles.GetAllRoles())
{
using (var group = GroupPrincipal.FindByIdentity(pc, role))
{
if (group != null)
{
if (adUser.IsMemberOf(group))
{
if (!Roles.IsUserInRole(model.UserName, role))
Roles.AddUserToRole(model.UserName, role);
}
else
{
if (Roles.IsUserInRole(model.UserName, role))
Roles.RemoveUserFromRole(model.UserName, role);
}
}
}
}
}
}
}
This works fine on our web-server which is connected to our domain server.
Now I set up an Windows Azure Active Directory and configured it to be synced with our On-Premise AD which also works.
But I am now struggeling on finding a way to connect my PrincipalContext to the WAAD.
Is this even possible and how? If not, what is the alternative?
I only found examples using Single-Sign-On which does this redirection to the MS login page we do NOT want to use, because we have a mixed authentication and depending on the entered user name it either uses the ASP.NET Membership or pulls the user and groups from AD (and actually creates an ASP.NET Membership user as seen above).
No.
You can't really use PrincipalContext with WAAD. Have to explicitly state here that you cannot currently (Jan. 2014) do direct user authentication against WAAD. You will need to rewrite some parts of your application to be compatible:
Authentication happens only on the WAAD side, your code cannot do user+password validation. This also happens on WAAD provided login page. You have limited control on how this page looks like and can customize it via Premium features of WAAD.
You can create users and reset user password using the WAAD Graph API.
Explore the Graph API for additional operations you might need (i.e. ask for user's group membership, direct reports, etc.)
You will have to switch from Windows Authentication to Federated Authentication. Depending on what VS version you are using this might be easy or tough. For VS 2012 there is Identity and Access Tool extension. While in 2013 authentication can only be configured when you create the project and cannot be altered afterwards. But you can copy configuration changes from other project over. You need changes in web.config file along with what is initialized in global.asax. Check it here - although about VS 2013 RC, the process is same in RTM.

Resources