OpenIdConnect error - Tenant identifiers may not be an empty GUID - asp.net

I'm attempting to use the OWIN Open ID Connect middleware to outsource authentication for my ASP.NET application to Azure Active Directory. The application successfully redirects to the Azure AD sign-in page upon accessing a page that requires authorisation. However upon signing in to Azure AD I'm encountering the following error:
AADSTS90002: Requested tenant identifier '00000000-0000-0000-0000-000000000000' is not valid. Tenant identifiers may not be an empty GUID.
Here is the call to UseOpenIdConnectAuthentication. The tenant which is included in the Authority URL is f82d0e29-6018-43c6-b806-1f46a009ff4a. So why does Azure AD think I am passing in an empty GUID for the tenant?
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
AuthenticationType = "AzureAd",
Caption = "Office 365",
Scope = "openid email profile",
ClientId = "e34401b2-6a9f-4547-9229-f0d93a33b1a1",
Authority = "https://login.windows.net/f82d0e29-6018-43c6-b806-1f46a009ff4a/",
PostLogoutRedirectUri = "http://localhost:44333",
RedirectUri = "http://localhost:44333",
AuthenticationMode = AuthenticationMode.Passive,
SignInAsAuthenticationType = signInAsType
});

There may be a bug in AAD right now. Another thread also started around the same time as this one: Empty GUID error when using Azure AD consent flow - According to one comment the AAD team is aware and looking into it.
UPDATE:
See my version of this question for the full diagnosis and solution:
Azure Active Directory passing empty GUID for tenantId with default template

Related

MSAL TokenAcquisition GetAccessTokenOnBehalfOfUser always fails because getaccounts is always empty

I have been trying to use Azure AD MSAL and ADAL and have NEVER been able to retrieve a token. I have tried the ALL of the samples and keep getting to the same issue, token is created, added to the EF cache DB but when the tokenAcquisition object tries to retrieve it, no account is found and fails to get token.
I have read through most (if not ALL) of the issues on GitHub and SO. this seems to be working for others but looks like numerous people have the same issue and I have yet to see an answer other then pointing me to the samples I have tried.
Simple question for the moment - how do I get accounts from the IConfidentialClientApplication?
I have NEVER been able to get a single account or a list of accounts.
Create app object:
var app = ConfidentialClientApplicationBuilder.CreateWithApplicationOptions(_applicationOptions)
.WithRedirectUri(currentUri)
.WithAuthority(authority)
.Build();
In GetAccessTokenOnBehalfOfUser:
IAccount account = await application.GetAccountAsync(accountIdentifier);
returns NULL
and
var accounts = await application.GetAccountsAsync();
returns an empty lists/IEnumerable.
I would expect to retrieve an account from
application.GetAccountAsync(accountIdentifier)
and a list from
await application.GetAccountsAsync();
OK, Finally found my issue.
The issue comes in using ASPNet identity logging into AzureAD as an external authority but using the identity to signin and create the claims principle.
I was mssing the AzureAD ObjectIdentifier from my claims. so the solution seems to be adding the ObjectIdentifier to the identity. I did this by using a ClaimsTransofrmation and looking for the auth type. If it was NOT Identity.Application it is from AzureAD and check to see if the User has the UserClaim and add it if not. This claim is then picked up and put in the principle's claims and under the covers, now the account is found....
if (principal.HasClaim(c => c.Type == SecurityConstants.ClaimTypes.ObjectId))
{
string oId = principal.FindFirstValue(SecurityConstants.ScpcClaimTypes.ObjectId);
var user = _usrMgr.FindByNameAsync(usrNm).Result;
List<Claim> claims = new List<Claim>(_usrMgr.GetClaimsAsync(user).Result);
if (!claims.Exists(c => c.Type == SecurityConstants.ScpcClaimTypes.ObjectId))
{
_usrMgr.AddClaimAsync(user, new Claim(SecurityConstants.ScpcClaimTypes.ObjectId, oId));
}

Azure mobile app custom authentication and MobileServiceClient

I was following custom authentication for my azure mobile app (https://www.newventuresoftware.com/blog/custom-authentication-with-azure-mobile-apps)
I created AuthControll which accepts username and password and creates token. When i call MobileServiceClient.InvokeApiAsync("Auth", loginInfoDictionary), I receive the user name and token succesfully. I created new MobileServiceUser(username) with received token an set it to MobileSeviceClient.CurrentUser. But When i call MobileServiceClient.InvokeApi over method which requiere authorization, it tells me i am unauthorized.
What i am supposed to do with received token then ? Can i use MobileServiceClient.InvokeApiAsync and MobileServiceClient.GetTable methods with this type of authorization ? If yes what i am missing ?
I found a problem in my solution on server side. I forgot to specify my url site when creating a token. Like this
var signingKey = Environment.GetEnvironmentVariable("WEBSITE_AUTH_SIGNING_KEY");
var audience = "https://TheSiteIForgotToSpecify.azurewebsites.net/"; // audience must match the url of the site
var issuer = "https://TheSiteIForgotToSpecify.azurewebsites.net/"; // audience must match the url of the site
JwtSecurityToken token = AppServiceLoginHandler.CreateToken(
claims, signingKey, audience, issuer, TimeSpan.FromHours(24));

Get token from ADFS

I'm trying to obtain a token from ADFS to that I can use it with an on-premise Windows Service Bus installation. I may not have ADFS properly configured because I get the following message:
MSIS3127: The specified request failed.
The code to access the token is as follows:
string adrecaSTS = "trust/13/usernamemixed";
WS2007HttpBinding binding = new WS2007HttpBinding();
binding.Security.Message.EstablishSecurityContext = false;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
binding.Security.Mode = SecurityMode.TransportWithMessageCredential; //https
string baseSSLUri = #"https://<myadfs>/adfs/services/";
WSTrustChannelFactory trustChannelFactory = new WSTrustChannelFactory(binding, new EndpointAddress(baseSSLUri + adrecaSTS));
trustChannelFactory.TrustVersion = TrustVersion.WSTrust13;
trustChannelFactory.Credentials.UserName.UserName = "username";
trustChannelFactory.Credentials.UserName.Password = "password";
WSTrustChannel tokenClient = (WSTrustChannel)trustChannelFactory.CreateChannel();
//create a token issuance issuance
RequestSecurityToken rst = new RequestSecurityToken(RequestTypes.Issue);
//call ADFS STS
SecurityToken token = tokenClient.Issue(rst);
The endpoint is enabled on ADFS and my client (laptop on separate domain) trusts the certificate from ADFS.
Do I need to set up some kind of trust or something further? This error message is not particularly helpful.
See here:
https://github.com/thinktecture/Thinktecture.IdentityServer.v2/blob/master/src/Libraries/Thinktecture.IdentityServer.Protocols/WSFederation/HrdController.cs
The ValidateToken method has most of the code - but you first need to extract the InnerXml from the generic token and turn that into a SAML security token (again using a token handler).
Found the issue. I was trying to log on as an administrator account. When I used a regular user it worked.
I also had to modify the RequestSecurityToken to have a KeyType of KeyType.Symmetric
I see that you solved your issue, but here is some additional inforamation to potentially help others that might have the same error message but a different cause.
The AD FS error, "MSIS3127...", can have multiple causes. For us, it was caused by one of our relying party claim rules specifying an AD FS attribute store that didn't exist.
In order to debug the error, we checked the Event Viewer on all of the servers running AD FS, and that's where we found the detailed message that called out the attribute store problem. So, if anyone else gets the same error message, then I suggest checking the Event Viewer on AD FS to see if there are additional logs.
Note that AD FS logs to the Event Viewer under the folder/node of Applications and Services Logs => AD FS => Admin

how can I use a Microsoft Account to authenticate to my website

I have a website where a users identity is needed, I'd really prefer not to make them create yet another username/password combo that they have to remember
are there SDK's for allowing authentication from an Microsoft account?
That's rather easy as a default empty template of an ASP.NET 4.5 website shows how to have OAuth2 authentication with google/facebook/liveid/twitter.
http://www.asp.net/aspnet/overview/aspnet-45/oauth-in-the-default-aspnet-45-templates
Check out the Principal Context class. You can create it using a localhost (Machine) or domain context and use the ValidateCrentials(string username, string password) method to authenticate using Windows credentials.
http://msdn.microsoft.com/en-us/library/bb154889.aspx
Here's how I've used it in my website. (Put this in a POST method of your authentication controller or something)
The code below will take a username say "bob" or "localhost\bob" or "DOMAIN\bob" etc., and get the right PrincipalContext for authenticating the user. NOTE: it's case insensitive here.
public bool ValidateCredentials(string username, System.Security.SecureString password)
{
string domain = Environment.MachineName;
if (username.Contains("\\"))
{
domain = username.Split('\\')[0];
username = username.Split('\\')[1];
}
if (domain.Equals("localhost", StringComparison.CurrentCultureIgnoreCase))
domain = Environment.MachineName;
if (domain.Equals(Environment.MachineName, StringComparison.CurrentCultureIgnoreCase))
using (PrincipalContext context = new PrincipalContext(ContextType.Machine))
{
return context.ValidateCredentials(username, password.ToUnsecureString());
}
else
using(PrincipalContext context = new PrincipalContext(ContextType.Domain))
{
//return context.ValidateCredentials(domain + "\\" + username, password.ToUnsecureString());
return context.ValidateCredentials(username, password.ToUnsecureString());
}
}
Microsoft provides the Live Connect SDK for integration Microsoft services into your applications, including the Microsoft Accounts identity provider.
There is a specific example on Server-Side Scenarios which should cover all you need to get integrated.
Do you mean from an active directory windows account? If so you could use windows authentication and just have the index page sign them in automatically.
http://msdn.microsoft.com/en-us/library/ff647405.aspx
Use the following commands in your code behind file to get the relevant information for signing in:
System.Security.Principal.WindowsIdentity.GetCurrent().Name
User.Identity.IsAuthenticated
User.Identity.AuthenticationType
User.Identity.Name
The amount of changes / rebranding / deprecation / dead links from Microsoft drives me crazy. In any case, the latest version of this from what I've found is "Microsoft Account external login", which can be first set up on the Microsoft Developer Portal.
I found a guide that explains how to do this for .Net Core at https://learn.microsoft.com/en-us/aspnet/core/security/authentication/social/microsoft-logins, though the first half (e.g. setting the Redirect URI) isn't framework-specific.
I also found some relevant source code for .Net Core at https://github.com/aspnet/Security/blob/master/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountOptions.cs, which shows some of the Claims (user details) that are retrieved:
ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "id");
ClaimActions.MapJsonKey(ClaimTypes.Name, "displayName");
ClaimActions.MapJsonKey(ClaimTypes.GivenName, "givenName");
ClaimActions.MapJsonKey(ClaimTypes.Surname, "surname");
ClaimActions.MapCustomJson(ClaimTypes.Email,
user => user.Value<string>("mail") ?? user.Value<string>("userPrincipalName"));
The support from the latest version of .Net Core suggests to me that this external login API still works. I haven't tested them out yet, I will update if I get to do this login integration.
Simply use "Live Connect" via Oauth 2.0:
http://msdn.microsoft.com/en-us/library/live/hh243647.aspx
or
https://dev.onedrive.com/

Check other user's role membership (IsInRole, WindowsIdentity/Principal)

I'm writing ASP.NET code to run on an internal network where Windows Authentication will be used. Certain operations will require me to run a group membership check on other users (not the current user)
NOTE: I am NOT trying to impersonate this account, or access any information in the context of this other user. Just trying to find out what kind of user they are for internal business logic.
My first thought was to use
new WindowsPrincipal(new WindowsIdentity("MACHINENAME\\username"))
.IsInRole("MACHINENAME\\Group1")
However, the WindowsIdentity constructor fails with a SecurityException "The name provided is not a properly formed account name".
If I strip MACHINENAME\ from the parameter, I get a different error: There are currently no logon servers available to service the logon request
The WindowsTokenRoleProvider role provider explicitly only works with the current user, and will not check other user accounts.
Are there security restrictions to checking roles of other users? Would it make a difference if the web server was on a domain and I were checking domain accounts?
In the end, I'll need to have this work on an AD domain, but would prefer a solution that will work on either local or AD accounts.
Thank you
UPDATE: I've been able to test this on a domain now -- the code does work in an AD context so long as I don't use the domain name (test "username" against "Group1", not "DOMAIN\username" against "DOMAIN\Group1")
So how would I get this to work in the context of local users and groups?
Based on Rob A's comment, PrincipalContext and UserPrincipal are the classes I apparently need to use:
using (PrincipalContext ctx = new PrincipalContext(ContextType.Machine))
{
var u = UserPrincipal.FindByIdentity(ctx, IdentityType.Name, "username");
var b = u.IsMemberOf(ctx, IdentityType.Name, "Group1");
var groups = u.GetAuthorizationGroups();
}
And by altering the ContextType, can switch between local accounts and AD accounts. I wish this was built into a RoleProvider, but I guess that's something I'd have to do for myself.

Resources