B2B users cannot sign in to Tenant using v2.0 endpoint & MSAL Auth flow - azure

I am trying to create a B2B Management portal. I've started off with this sample since it uses MSAL and Graph API.
user#live.se is in the tenant. It's been invited as a "guest user", i.e a B2B user. However, signing in with user#live.se does not work even though it's been added to the tenant. Following error after sign-in:
AADSTS50020: User account 'user#live.se' from external identity provider 'live.com' is not supported for api version '2.0'. Microsoft account pass-thru users and guests are not supported by the tenant-independent endpoint. Trace ID: 2ad8bee0-d00a-4896-9907-b5271a113300 Correlation ID: 0ea84617-4aa1-4830-859f-6f418252765e Timestamp: 2017-10-03 15:35:22Z
I changed the authority (from common) to only allow users from my tenant (requirement):
https://login.microsoftonline.com/tenant.onmicrosoft.com/v2.0
Do guests not count as part of my tenant when using MSAL? that would mean I have to use "old" tech, i.e ADAL and AAD Graph, which is not recommended, and feels kinda lame.

If you pass the specific tenant value in the authority, then
Only users with a work or school account from a specific Azure AD tenant can sign in to the application. Either the friendly domain name of the Azure AD tenant or the tenant's GUID identifier can be used.
That's means the Microsoft Account is not supported in this scenario. Refer here for the Microsoft Account and Work or school accounts. And in this scenario, if you new a user user from other tenant, it should also works.
You can refer the document for tenant from link below:
Fetch the OpenID Connect metadata document

I know this is an old thread but just in case anyone stumbles upon it, here is a solution:
In cases of Personal guest accounts, use Credential Grant Flow (Get access without a user).
To do that, you would first need to grant appropriate permission (of Type Application) for the API you wanted to use on behalf of the signing user. This would let you acquire access token with the application's identity itself rather than the signed in user.
Next get token like this (in this sample, I'm getting access token for Graph API):
public async Task<string> GetAccessToken()
{
using (HttpClient httpClient = new HttpClient())
{
string token = "";
try
{
httpClient.BaseAddress = new Uri($"https://login.microsoftonline.com/{tenantId}");
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
HttpRequestMessage request = new HttpRequestMessage();
List<KeyValuePair<string, string>> body = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("client_id", clientId),
new KeyValuePair<string, string>("scope", "https://graph.microsoft.com/.default"),
new KeyValuePair<string, string>("client_secret", appSecret),
new KeyValuePair<string, string>("grant_type", "client_credentials")
};
request.Method = HttpMethod.Post;
request.RequestUri = new Uri($"{httpClient.BaseAddress}/oauth2/v2.0/token");
request.Content = new FormUrlEncodedContent(body);
var response = await httpClient.SendAsync(request);
var content = await response.Content.ReadAsAsync<dynamic>();
token = content.access_token;
}
catch (Exception e)
{
}
return token;
}
}
Tip: If your goal is also Graph API, don't try to get logged in user info by using the /me endpoint in this case. Since the token was generated using the application identity rather than the signed in user, /me would be the application not the logged in user. What you want to do is: retrieve logged in user id from the Claim (Type: http://schemas.microsoft.com/identity/claims/objectidentifier) and use the /user/{userid} endpoint.

I found: for personal accounts (Get access without a user) in the body of the request you must to use grant_type = 'client_credentials' and for corporate accounts to use grant_type = 'authorization_code'

Related

DocuSign JWT Grant flow Consent_Required for blanket consent

We have implemented JWT grant flow and provided blanket consent for integration key but when impersonating registered and active user under this account getting consent required error.
We have SSO enabled
All users are with same domain
Blanket consent provided with impersonation signature scope
Here how I am trying to get access token using DocuSign's C# SDK for impersonated user:
string BaseUrl = "https://demo.docusign.net/restapi";
string oAuthBasePath = "account-d.docusign.com";
var apiClient = new DocuSign.eSign.Client.ApiClient(BaseUrl, oAuthBasePath, null);
//Get access token using admin account
OAuth.OAuthToken tokenInfo = apiClient.RequestJWTUserToken(Integration_Client_Key, UserId, oAuthBasePath, Encoding.UTF8.GetBytes(RSAPrivateKey), 1, Scopes);
OAuth.UserInfo userInfo = apiClient.GetUserInfo(tokenInfo.access_token);
var account = userInfo.Accounts.FirstOrDefault(a => a.AccountId == AccountId);
apiClient = new ApiClient(BaseUrl, oAuthBasePath, null);
//Get user's UserId(GUID) to impersonate
var impersonateUserId = await SignatureHandlerEmailId(signatureHandlerEmailId, tokenInfo.access_token);
//Get access token using impersonate userId
tokenInfo = apiClient.RequestJWTUserToken(Integration_Client_Key, impersonateUserId, oAuthBasePath, Encoding.UTF8.GetBytes(RSAPrivateKey), 1, Scopes);
userInfo = apiClient.GetUserInfo(tokenInfo.access_token); //-- Here exception getting thrown for consent
Is there any wrong url I am passing or anything additional configuration need to do before sending request?
You have SSO and a claimed domain for demo.docusign.net?
You're using the demo IdP (account-d.docusign.com). It is a completely different IdP than the production IdP, account.docusign.com.
Since you're using account-d.docusign.com, you need to claim the email domain for it using the demo.docusign.net org admin tool.
Added: checking your claimed email domains
This info is available via the Domains section of the DocuSign organization administration tool.
See the docs.
Added some more
You can also programmatically get a list of the organization's claimed domains via the Org Admin API method ReservedDomains:getReservedDomains
Another condition will be to claim a domain. You can refer to this how-to guide
Also this blog post has a great info

How to find Audience field for Active Directory OAuth Authentication? (How to send a post request to DevOps from Azure Logic App?)

Please help me with this problem.
I'm trying to send a post request from Azure Logic App to the DevOps to create a release.
I created an http action in my Logic App, This is the uri for creating a release:
https://vsrm.dev.azure.com/{organization}/{project}/_apis/release/releases?api-version=5.0
I'm using Active Directory OAuth for authentication, which I need to provide tenant, client id, audience and secret.
I'm using tenant, client id and secret of my application in Azure Active Directory, but I'm not sure what to use for audience.
Can some one explain for me how to find this audience field?
Do I need to do other things to connect to my DevOps? or define permissions or any other parameters for header?
There will be two approaches for getting authenticated.
Use Azure AD Authentication.
The resource for DevOps is a static value: 499b84ac-1321-427f-aa17-267ca6975798. But, as the DevOps REST API can only set with delegated permission.
You need to use password grant flow to get token:
The token you get will be a bearer token.
The other option is to use personal access token. You can create one in DevOps portal.
And then use it as following:
try
{
var personalaccesstoken = "PAT_FROM_WEBSITE";
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(
System.Text.ASCIIEncoding.ASCII.GetBytes(
string.Format("{0}:{1}", "", personalaccesstoken))));
using (HttpResponseMessage response = await client.GetAsync(
"https://dev.azure.com/{organization}/_apis/projects"))
{
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseBody);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
Token you get in this way is a basic token.

Scope needed to get access token to pull user groups from Azure using client id and client secret and graph API

I am trying to get the groups in my azure tenant using my client secret and client id. Problem is I don't know what scope to pass when getting an access token to use the graph api. I used https://graph.microsoft.com/.default to get an access token but this doesn't include permission to pull groups. What's the appropriate scope to use
https://learn.microsoft.com/en-us/graph/api/group-list?view=graph-rest-1.0#permissions
Permissions
One of the following permissions is required to call this API. To learn more, including how to choose permissions, see Permissions.
Permission type Permissions (from least to most privileged)
Delegated (work or school account) Group.Read.All, Group.ReadWrite.All
Application Group.Read.All, Group.ReadWrite.All
You need to configure the API access within AAD, not with the scope. Make sure that you don't forget to click on "grant permissions".
Example assumes that you require application permission. Delegated permission works similarly.
Sample code for getting the data using MSAL for authentication:
IConfidentialClientApplication app = new ConfidentialClientApplication(
"clientId",
"https://login.microsoftonline.com/yourtenant.onmicrosoft.com",
"http://localhost (redirecturi)",
new ClientCredential("secret"),
new TokenCache(), new TokenCache());
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
try
{
AuthenticationResult result = await app.AcquireTokenForClientAsync(scopes);
System.Console.WriteLine(result.AccessToken);
using (var http = new HttpClient())
{
http.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", result.AccessToken);
var groupResponse = await http.GetAsync("https://graph.microsoft.com/v1.0/groups");
var groupJson = await groupResponse.Content.ReadAsStringAsync();
System.Console.WriteLine(groupJson);
}
}
catch (Exception ex)
{
System.Console.WriteLine(ex.Message);
}

How to login to a Azure Active Directory as a User in Test-Code?

I'm new to Azure and struggle a little in learning all the functionalities of the Azure Active Directory (AAD), so I hope you can clear some things up for me. Here is what I already did:
I registered a web app which serves as a resource provider and offers different APIs behind a API management service.
The web app has several users and roles in the AAD. Plus, more detailed permissions are set on App-Level. So the AAD doesn't control all permissions of my users.
Users are authenticated by using OAuth 2.0. In practice, this means if a new user tries to login to my app he gets redirected to Microsofts login page, enters username and password and then gets a JWT token from Microsofts authentication server.
Now what I want to do:
I want to write an app running on my build server which tests the user permissions. The app has to be written in C# .NET Core. Now I'm struggling on how to log in as a user from my code, so my question is:
How can i log in as a user from code to AAD and get the JWT token to test the user permissions? Can I do this by just using username / password, or do I need to register my test app in the AAD? What are the best solutions to reach my goals?
Thank you in advance
Juunas' comment already covered most of what is required. Just putting a bit more detail behind it.
You can use MSAL (link) to write a .NET Core application that accesses your API.
Within MSAL, you need to use username password authentication (Resource Owner Password Credentials grant) to acquire a JWT token. Please never use this grant outside your testing application.
Depending on how your app is configured, using just the clientId of the API could be enough. It would however be best practice to register a separate native app.
Some wording to help you along:
ClientId: The id of the client application which is requesting the token.
Scope: The scope of the API you acquire the token for. Should already be configured somewhere in your API. Usually something with the AppId URI. Possible examples could look like:
https://<yourtenant>.onmicrosoft.com/<yourapi>/user_impersonation
https://<clientId-of-API>/.default
...
Authority: Your AAD, e.g. https://login.microsoftonline.com/yourtenant.onmicrosoft.com
Code example for the password grant from the wiki (more examples there):
static async Task GetATokenForGraph()
{
string authority = "https://login.microsoftonline.com/contoso.com";
string[] scopes = new string[] { "user.read" };
PublicClientApplication app = new PublicClientApplication(clientId, authority);
try
{
var securePassword = new SecureString();
foreach (char c in "dummy") // you should fetch the password
securePassword.AppendChar(c); // keystroke by keystroke
result = await app.AcquireTokenByUsernamePasswordAsync(scopes, "joe#contoso.com",
securePassword);
}
catch(MsalException)
{
// See details below
}
Console.WriteLine(result.Account.Username);
}
I actually find out a way to do it in "pure" C# without using the MSAL library, which I had some trouble with. So if you're looking for a solution w/o MSAL, you can do it the way described below.
Prerequisites
A user must exist in the AAD and must not use a Microsoft Account (source in Active Directory must not be "Microsoft Account").
A client application must be registered in the Azure Active Directory. The client app must be granted permissions to the app you want to test. If the client app is of type "Native", no client secret must be provided. If the client app is of type "Web app / api", a client secret must be provided. For testing purposes, its recommended to use an app of type "Native" without a client secret.
There must be no two factor authentication.
C# Code
You can than create a class "JwtFetcher" and use code like this:
public JwtFetcher(string tenantId, string clientId, string resource)
{
this.tenantId = !string.IsNullOrEmpty(tenantId) ? tenantId : throw new ArgumentNullException(nameof(tenantId));
this.clientId = !string.IsNullOrEmpty(clientId) ? clientId : throw new ArgumentNullException(nameof(clientId));
this.resource = !string.IsNullOrEmpty(resource) ? resource : throw new ArgumentNullException(nameof(resource));
}
public async Task<string> GetAccessTokenAsync(string username, string password)
{
var requestContent = this.GetRequestContent(username, password);
var client = new HttpClient
{
BaseAddress = new Uri(ApplicationConstant.Endpoint.BaseUrl)
};
var message = await client.PostAsync(this.tenantId + "/oauth2/token", requestContent).ConfigureAwait(false);
message.EnsureSuccessStatusCode();
var jsonResult = await message.Content.ReadAsStringAsync().ConfigureAwait(false);
dynamic objectResult = JsonConvert.DeserializeObject(jsonResult);
return objectResult.access_token.Value;
}
private FormUrlEncodedContent GetRequestContent(string username, string password)
{
List<KeyValuePair<string, string>> requestParameters = new List<KeyValuePair<string, string>>()
{
new KeyValuePair<string, string>(ApplicationConstant.RequestParameterName.GrantType, ApplicationConstant.RequestParameterValue.GrantTypePassword),
new KeyValuePair<string, string>(ApplicationConstant.RequestParameterName.Username, username),
new KeyValuePair<string, string>(ApplicationConstant.RequestParameterName.Password, password),
new KeyValuePair<string, string>(ApplicationConstant.RequestParameterName.ClientId, this.clientId),
new KeyValuePair<string, string>(ApplicationConstant.RequestParameterName.Resource, this.resource)
};
var httpContent = new FormUrlEncodedContent(requestParameters);
return httpContent;
}
The grant type for this is just "password".

Invite user once created in the AD B2C to sign on my app

I have this scenario working properly on ADB2C following this tutorial
I can create users using ADAL.
(here is the context for the scenario)
My goal is to send user invitation (email) to get him on board on my appp.
I found that on graph.microsoft.com (the MS Graph and not the AD graph) there is the invitation manager that can be used for that purpose and may be is the way that invitation gets triggered if you create user on the B2C azure portal.
So Do I need to give permissions to my app (the same way I did to get
access token on AD graph to manage users) ?
Should I acquire the token on the MS graph the same way I did for AD
AuthorizationCodeReceived = async (context) =>
{
// get authentication context
string userObjectID = context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
AuthenticationContext authContext = new AuthenticationContext($"https://login.microsoftonline.com/{AuthenticationHelper.Tenant}", new NaiveSessionCache(userObjectID));
ClientCredential credential = new ClientCredential(AuthenticationHelper.ClientId, AuthenticationHelper.AppKey);
AuthenticationResult result = await authContext.AcquireTokenAsync("https://graph.windows.net", credential);
// ----> Token used on the authorization header for AD user management and work properly
AuthenticationHelper.Token = result.AccessToken;
// Token for MS graph
ClientCredential MSCredential = new ClientCredential(AuthenticationHelper.MSClientId, AuthenticationHelper.MSAppKey);
AuthenticationResult resultMSGraph = await authContext.AcquireTokenAsync("https://graph.microsoft.com", MSCredential);
// ----> Token used on the authorization header for MS Graph and is not working !!
AuthenticationHelper.MSGraphToken = resultMSGraph.AccessToken;
},
Thanks for your help
The invitation manager API on the Microsoft Graph is not supported for Azure AD B2C.
At this time, the invitation manager API is intended for enterprise/regular Azure AD tenants to invite other users as guests (see Azure AD B2B Collaboration).
There is already an entry in the Azure AD B2C UserVoice forum asking for the ability to send email invitation for new users to sign up. I'd recommend you vote for this entry to help us prioritize it and also to stay up to date on it's progress.
In the interim, you'll have to implement this yourself, either a simple welcome email or a more complex "redeem code" workflow.

Resources