I've been trying to integrate my Android Application with Azure AD B2C using MSAL but I can't figure out how to retrieve my access token using a custom scope. I'm using this MSAL samples as a reference.
I already granted my application the needed permissions but i still get this message:
No access is found for scopes: https://rbxsoftb2c.onmicrosoft.com/rbx/get_access_token
Here's my Azure B2C settings, MSAL Constants and the API I'm trying to access :
MSAL Constants :
public class Constants {
/* Azure AD b2c Configs */
final static String AUTHORITY = "https://login.microsoftonline.com/tfp/%s/%s";
final static String TENANT = "rbxsoftb2c.onmicrosoft.com";
final static String CLIENT_ID = "MY_CLIENT_ID";
final static String SCOPES = "https://rbxsoftb2c.onmicrosoft.com/rbx/get_access_token";
final static String API_URL = "https://rbxsoft.azurewebsites.net/api/values";
final static String SISU_POLICY = "B2C_1_SiUpIn";
final static String EDIT_PROFILE_POLICY = "B2C_1_edit_profile";
}
My API :
[Produces("application/json")]
[Route("api/Values")]
[Authorize]
public class ValuesController : Controller
{
CosmosDatabaseConnection cdc = CosmosDatabaseConnection.Instance;
// GET: api/Values
[HttpGet]
public JValue Get()
{
AppState appState = SessionManager.GetCookieSession(HttpContext.Session, User);
return new JValue ( appState.User.profile.ToString() );
}
}
Related
Below is the output from an error which is being generated whilst trying to authenticate credentials with Azure for allowing public access to a Power BI Embedded report:
It has initially displayed a pop to confirm that I want to give but after I have given permission the above error is displayed.
I have checked the credentials I have entered in the appsettings.json file and have confirmed that they are correct.
In the Startup.cs file I have added this section:
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"), "AzureAd")
.EnableTokenAcquisitionToCallDownstreamApi(Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' '))
.AddMicrosoftGraph(Configuration.GetSection("DownstreamApi"))
.AddInMemoryTokenCaches();
services.AddScoped(typeof(PowerBiServiceApi));
services.AddRazorPages()
.AddMicrosoftIdentityUI();
The error itself is being generated whilst acquiring the access token in the PowerBiServiceApi the code for which is provided below:
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Identity.Web;
using Microsoft.PowerBI.Api;
using Microsoft.PowerBI.Api.Models;
using Microsoft.Rest;
namespace NDTSM2.Services.Implementations.PowerBI
{
// A view model class to pass the data needed to embed a single report.
public class EmbeddedReportViewModel
{
public string Id;
public string Name;
public string EmbedUrl;
public string Token;
}
public class PowerBiServiceApi
{
private ITokenAcquisition tokenAcquisition { get; }
private string urlPowerBiServiceApiRoot { get; }
public PowerBiServiceApi(IConfiguration configuration, ITokenAcquisition tokenAcquisition)
{
this.urlPowerBiServiceApiRoot = configuration["PowerBi:ServiceRootUrl"];
this.tokenAcquisition = tokenAcquisition;
}
public const string powerbiApiDefaultScope = "https://analysis.windows.net/powerbi/api/.default";
// A method to get the Azure AD token (also known as 'access token')
public string GetAccessToken()
{
return this.tokenAcquisition.GetAccessTokenForAppAsync(powerbiApiDefaultScope).Result;
}
public PowerBIClient GetPowerBiClient()
{
var tokenCredentials = new TokenCredentials(GetAccessToken(), "Bearer");
return new PowerBIClient(new Uri(urlPowerBiServiceApiRoot), tokenCredentials);
}
public async Task<EmbeddedReportViewModel> GetReport(Guid WorkspaceId, Guid ReportId)
{
PowerBIClient pbiClient = GetPowerBiClient();
// Call the Power BI service API to get the embedding data
var report = await pbiClient.Reports.GetReportInGroupAsync(WorkspaceId, ReportId);
// Generate a read-only embed token for the report
var datasetId = report.DatasetId;
var tokenRequest = new GenerateTokenRequest(TokenAccessLevel.View, datasetId);
var embedTokenResponse = await pbiClient.Reports.GenerateTokenAsync(WorkspaceId, ReportId, tokenRequest);
var embedToken = embedTokenResponse.Token;
// Return the report embedded data to caller
return new EmbeddedReportViewModel
{
Id = report.Id.ToString(),
EmbedUrl = report.EmbedUrl,
Name = report.Name,
Token = embedToken
};
}
}
}
Does anyone have any ideas why the error is being generated (have looked for guidance but so far none of the advice has rectified the issue)?
Any help would be very much appreciated.
Further to original question:
Error Details:
Operation returned an invalid status code 'Unauthorized'
at Microsoft.PowerBI.Api.ReportsOperations.GetReportInGroupWithHttpMessagesAsync(Guid groupId, Guid reportId, Dictionary`2 customHeaders, CancellationToken cancellationToken)
at Microsoft.PowerBI.Api.ReportsOperationsExtensions.GetReportInGroupAsync(IReportsOperations operations, Guid groupId, Guid reportId, CancellationToken cancellationToken)
at Microsoft.PowerBI.Api.ReportsOperationsExtensions.GetReportInGroup(IReportsOperations operations, Guid groupId, Guid reportId)
at NDTSM2.Services.Implementations.PowerBI.PbiEmbedService.GetEmbedParams(Guid workspaceId, Guid reportId, Guid additionalDatasetId) in C:\Users\cryof\Desktop\NDTMS4\Service\NDTSM2.SERVICES\Implementations\PowerBI\PbiEmbedService.cs:line 41
at NDTMS2.Web.Controllers.EmbedInfoController.GetEmbedInfo() in C:\Users\cryof\Desktop\NDTMS4\NDTMS2.WEB\Controllers\EmbedInfoController.cs:line 40
The line that is generating the error in the PbiEmbedService is this:
var pbiReport = pbiClient.Reports.GetReportInGroup(workspaceId, reportId);
Follow the sample here
// For app only authentication, we need the specific tenant id in the authority url
var tenantSpecificUrl = azureAd.Value.AuthorityUrl.Replace("organizations", azureAd.Value.TenantId);
// Create a confidential client to authorize the app with the AAD app
IConfidentialClientApplication clientApp = ConfidentialClientApplicationBuilder
.Create(azureAd.Value.ClientId)
.WithClientSecret(azureAd.Value.ClientSecret)
.WithAuthority(tenantSpecificUrl)
.Build();
// Make a client call if Access token is not available in cache
authenticationResult = clientApp.AcquireTokenForClient(azureAd.Value.ScopeBase).ExecuteAsync().Result;
Similar issue here. I have checked the answer and try to implement all the possible forms of link in my startup.cs class with the following code:
var idClient = ConfidentialClientApplicationBuilder.Create(appId)
.WithRedirectUri(redirectUri)
.WithTenantId(tenantId)
.WithClientSecret(appSecret)
.WithAuthority(Authority) // Authority contains the link as mentioned in the page(link attached above)
.Build();
I still get the similar error:
"OpenIdConnectMessage.Error was not null, indicating an error. Error: 'invalid_request'. Error_Description (may be empty): 'AADSTS50194: Application 'xxx-xxx-xxx-xxx-xxxx'(ASPNET-Quickstart) is not configured as a multi-tenant application. Usage of the /common endpoint is not supported for such applications created after '10/15/2018'. Use a tenant-specific endpoint or configure the application to be multi-tenant.
Trace ID: xxx-xxx-xxx-xxx-xxxx
Correlation ID: xxx-xxx-xxx-xxx-xxxx
Timestamp: 2022-06-11 05:33:24Z'. Error_Uri (may be empty): 'error_uri is null'."
The combination of links I have used in variable Authority are the following: "https://login.microsoftonline.com/MY_TENANT_NAME" and "https://login.microsoftonline.com/MY_TENANT_ID"
I am being redirect to login page but after entering credentials OnAuthenticationFailedAsync method is being executed. This is the code of my startup class:
[assembly: OwinStartup(typeof(Web.Startup))]
namespace Web
{
public partial class Startup
{
// Load configuration settings from PrivateSettings.config
private static string appId = ConfigurationManager.AppSettings["ida:AppId"];
private static string appSecret = ConfigurationManager.AppSettings["ida:AppSecret"];
private static string redirectUri = ConfigurationManager.AppSettings["ida:RedirectUri"];
private static string graphScopes = ConfigurationManager.AppSettings["ida:AppScopes"];
private static string tenantId = ConfigurationManager.AppSettings["ida:tenantId"];
private static string aadInstance = EnsureTrailingSlash(ConfigurationManager.AppSettings["ida:AADInstance"]);
public static string Authority = "https://graph.microsoft.com/"+ tenantId;
string graphResourceId = "https://graph.microsoft.com/";
public void Configuration(IAppBuilder app)
{
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=316888
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = appId,
Authority = "https://login.microsoftonline.com/common/v2.0",
Scope = $"openid email profile offline_access {graphScopes}",
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
TokenValidationParameters = new TokenValidationParameters
{
// For demo purposes only, see below
ValidateIssuer = true
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailedAsync,
AuthorizationCodeReceived = OnAuthorizationCodeReceivedAsync
}
}
);
}
private static Task OnAuthenticationFailedAsync(AuthenticationFailedNotification<OpenIdConnectMessage,
OpenIdConnectAuthenticationOptions> notification)
{
notification.HandleResponse();
string redirect = $"/Home/Error?message={notification.Exception.Message}";
if (notification.ProtocolMessage != null && !string.IsNullOrEmpty(notification.ProtocolMessage.ErrorDescription))
{
redirect += $"&debug={notification.ProtocolMessage.ErrorDescription}";
}
notification.Response.Redirect(redirect);
return Task.FromResult(0);
}
private async Task OnAuthorizationCodeReceivedAsync(AuthorizationCodeReceivedNotification notification)
{
var idClient = ConfidentialClientApplicationBuilder.Create(appId)
.WithRedirectUri(redirectUri)
.WithTenantId(tenantId)
.WithClientSecret(appSecret)
.WithAuthority(Authority)
.Build();
string email = string.Empty;
try
{
string[] scopes = graphScopes.Split(' ');
var result = await idClient.AcquireTokenByAuthorizationCode(
scopes, notification.Code).ExecuteAsync();
email = await GraphHelper.GetUserDetailsAsync(result.AccessToken);
}
catch (MsalException ex)
{
System.Diagnostics.Trace.TraceError(ex.Message);
}
notification.HandleResponse();
notification.Response.Redirect($"/Account/SignInAzure?email={email}");
}
private static string EnsureTrailingSlash(string value)
{
if (value == null)
{
value = string.Empty;
}
if (!value.EndsWith("/", StringComparison.Ordinal))
{
return value + "/";
}
return value;
}
}
}
My application is for single tenant so please don't suggest me to change the setting and make it for multi-tenant.
Please check below points:
After trying to change it to specific tenant i.e.;
After changing to Ex: - https://login.microsoftonline.com/contoso.onmicrosoft.com (or tenant id),
please save changes ,refresh portal / everything and try again.
If still it shows the error , check if the Application is registered to the Azure AD Tenant as Multi Tenant Application.
Then if it still remains check if the account is actually on Azure
AD ,as this error can occur when the user credentials you are trying
to use does not belong to the same tenant where the application is
actually registered in.
If it is different tenant and you are trying to access from different
account, then you may need to change its supported account types to
any organizational directory or you need to check for correct
credentials. If not check everything or create a new app registration
.
Also please check this "Use a tenant-specific endpoint or configure the application to be multi-tenant" when signing into my Azure website for possible
ways to solve the issue.
Else you can raise a support request
References:
msal - MsalException: Applicationis not configured as a multi-tenant
application. Android - Stack Overflow
Use single-tenant Azure AD apps with Microsoft Graph Toolkit -
Waldek Mastykarz
I have been trying to use Azure AD B2C with my Xamaerin.Forms iphone application. I've got it to sort of work following along based on this sample: active directory b2c xamarin native
The sample, though takes me to a login page that seems to only accept Microsoft Logins like this one:
This page seems to only let people log in with existing Microsoft accounts. I have set up my app to accept local email accounts, and I want the sign in page to look more like the link provided on the Azure AD B2C page:
This second version is the part of the login page that is displayed when using the "run now endpoint" on the AD B2C signin signup policy that looks as follows: https://login.microsoftonline.com/crowdwisdom.onmicrosoft.com/oauth2/v2.0/authorize?p=B2C_1_susi&client_id=0729f822-6c97-4b94-b75c-df4259b0f3c5&nonce=defaultNonce&redirect_uri=https%3A%2F%2Flogin.crowdwisdom.co&scope=openid&response_type=id_token&prompt=login
I don't understand which parameter of the AcquireTokenAsync method determines which page is delivered to the app
Here is the code I run that results in the top example:
public async void HandleSignIn()
{
try
{
AuthenticationResult ar = await App.PCA.AcquireTokenAsync(Constants.Scopes, GetUserByPolicy(App.PCA.Users, Constants.PolicySignUpSignIn), Constants.UiParent);
}
catch (Exception ex)
{
// Checking the exception message
// should ONLY be done for B2C
// reset and not any other error.
if (ex.Message.Contains("AADB2C90118"))
HandlePasswordReset();
// Alert if any exception excludig user cancelling sign-in dialog
else if (((ex as MsalException)?.ErrorCode != "authentication_canceled"))
throw ex;
}
}
private IUser GetUserByPolicy(IEnumerable<IUser> users, string policy)
{
foreach (var user in users)
{
string userIdentifier = Base64UrlDecode(user.Identifier.Split('.')[0]);
if (userIdentifier.EndsWith(policy.ToLower())) return user;
}
return null;
}
Constants definition:
public static class Constants
{
public static string Tenant = "foo.onmicrosoft.com";
public static string ClientID = "0729...-..."; //actual client id here.
public static string PolicySignUpSignIn = "B2C_1_susi";
public static string PolicyEditProfile = "B2C_1_edit_profile";
public static string PolicyResetPassword = "B2C_1_reset";
public static string[] Scopes = { "User.read" };
public static string ApiEndpoint = "https://foo.azurewebsites.net";
public static string AuthorityBase = $"https://login.microsoftonline.com/{Tenant}/oauth2/v2.0/authorize?p=";
private static string suffix = $"&client_id={ClientID}&nonce=defaultNonce&redirect_uri=https%3A%2F%2Fmyapi&scope=openid&response_type=id_token&prompt=login";
public static string Authority = $"{AuthorityBase}{PolicySignUpSignIn}{suffix}";
public static string AuthorityEditProfile = $"{AuthorityBase}{PolicyEditProfile}";
public static string AuthorityPasswordReset = $"{AuthorityBase}{PolicyResetPassword}";
public static UIParent UiParent = null;
}
Thanks for posting the code. When you use MSAL with AAD B2C, it's important that you indicate which policy you wish to use. That's how MSAL knows to invoke B2C functionality. What's happening right now is that you're not properly indicating which policy to use, and MSAL is defaulting back to the regular Microsoft login page, which only allows Microsoft personal & work/school accounts.
When using MSAL, the proper way to indicate policy is to use tfp in the path of the authority, like:
string BaseAuthority = "https://login.microsoftonline.com/tfp/mytenant.onmicrosoft.com/mypolicy";
See https://github.com/Azure-Samples/active-directory-b2c-xamarin-native/blob/master/UserDetailsClient/UserDetailsClient/App.cs#L25 for the most up-to-date example.
Yes, you can also use the p query string parameter to indicate policy, but the way you are passing it to MSAL causes MSAL to ignore it's existence and not include it in OAuth requests.
Last comment: you shouldn't have to deal with all those OAuth parameters in your suffix variable. MSAL will take care of that stuff for you.
I'm trying to use OAuth bearer authentication in a Web API application, everything works fine on my local IIS, I'm able to get the token as you can see here:
But when I publish my project to a ApiApp in Azure it doesn't work at all. I get the response:
{
"message": "No HTTP resource was found that matches the request URI 'https://mysite.azurewebsites.net/API/login'.",
"messageDetail": "No type was found that matches the controller named 'login'."
}
As shown in here:
My Startup.Auth class looks like:
public partial class Startup
{
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
public static string PublicClientId { get; private set; }
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/login"),
Provider = new ApplicationOAuthProvider(),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// In production mode set AllowInsecureHttp = false
//#if DEBUG
AllowInsecureHttp = true
//#endif
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthAuthorizationServer(OAuthOptions);
}
}
I hope you can tell me what I'm doing wrong.
I've just find out that I was using an incorrect url. I was using /api/login instead of just /login.
I am trying to obtain authorization token from Azure AD through DotNetOpenAuth library. I do not want to use ADAL because I have a huge project in .net 3.5 and ADAL does not supports .net 3.5 (only .net > 4). However, I can't quite get it to work with Azure AD. I do not know what to configure. So far, this is what I have:
private static WebServerClient _webServerClient;
private static string _accessToken;
// Client ID (as obtained from Azure AD portal)
private static string clientId = "here goes my client id guid";
// Client Secret (as obtained from Azure AD portal)
private static string appKey = "here goes my secret";
private static string aadInstance = "https://login.microsoftonline.com/{0}";
private static string tenant = "mytenant.domain.com";
private static string authority = string.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
// Azure AD resource I am trying to access
private static string serviceResourceId = "https://mytenant.domain.com/protectedresource";
private static void InitializeWebServerClient()
{
var authorizationServer = new AuthorizationServerDescription
{
AuthorizationEndpoint = new Uri(""/* WHAT TO PUT HERE */),
TokenEndpoint = new Uri(""/* WHAT TO PUT HERE */)
};
_webServerClient = new WebServerClient(authorizationServer, clientId, appKey);
}
private static void RequestToken()
{
var state = _webServerClient.GetClientAccessToken();
_accessToken = state.AccessToken;
}
static void Main(string[] args) {
InitializeWebServerClient();
RequestToken();
}
The problem is I do not know what to place here. I do not know what values I should place here:
AuthorizationEndpoint = new Uri(""/* WHAT TO PUT HERE */),
TokenEndpoint = new Uri(""/* WHAT TO PUT HERE */)
Check if this GitHub sample assists you with authentication. It has 3 methods for authenticating and acquiring authentication token with detailed instructions. Check the app.config for sample values and method comments for details on what is required.
Link to the sample: Azure Authentication GitHub Sample
Related blog for the sample: Azure Authentication - Authenticating any Azure API Request in your Application
I believe the two endpoints you want are:
https://login.windows.net/{{tenantId}}/oauth2/authorize
https://login.windows.net/{tenantId}/oauth2/token
Where {tenantId} is your tenant's GUID identifier. It may also work with your domain as well, but I haven't checked that.