Azure Active Directory Token URL Path? - azure

I have a Single Page Application that communicates with a Web API that I am attempting to secure using Azure Active Directory. I've done this before using a local SQL database with local accounts and OAuth2 but I've never done it with Azure Active Directory.
I created a new MVC Web API project in Visual Studio 2017 and set the authentication part to "Cloud - Single Organization" and entered the corresponding domain. The API had me sign in during the creation process. The login information I provided was entered into the Web.Config file.
<appSettings>
<add key="ida:Tenant" value="domain.net" />
<add key="ida:Audience" value="domain.net/WebAPI" />
<add key="ida:ClientID" value="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" />
<add key="ida:Password" value="XXXXXXXXXXXXXXXXXXXXXXXXXXX" />
</appSettings>
Then in the new Startup.Auth.cs file I have the following:
public partial class Startup
{
// For more information on configuring authentication, please visit https://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
TokenValidationParameters = new TokenValidationParameters {
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"]
},
});
}
}
I don't see a path that I can call in there to initiate a login. In my original project it was the /Token path which is shown below from my original Startup.Auth.cs file.
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromHours(12),
RefreshTokenProvider = new ApplicationRefreshTokenProvider(),
};
In my original project in order to initiate the login I would use /Token in an Ajax call and pass along the username and password.
$.ajax({
url: url.getURL() + '/token',
method: 'POST',
data: {
username: username,
password: password,
grant_type: 'password'
},
How do I initiate the login in the new Web API which is using Azure Active Directory? I don't see a /token path or anything similar in the Startup.Auth.cs file. Is this something I have to add or is the project setup to automatically use the login credentials I entered when the project was first created?

When tying a Web API into Azure Active Directory there is no "/token" URL to call to initiate a login. You have to direct the users to the following website:
http://login.microsoftonline.com/{domain}/oauth2/v2.0/authorize?client_id={client_id}&response_type=id_token&scope=openid&response_mode=fragment&state=12345&nonce=678910
Make sure you enter the actual domain and client_id variables for your app which are listed in Azure. Also, in Azure, make sure the Reply URLs are set. The API will redirect back to your site after the login is complete. This does work for localhost.

Related

Azure Key Vault not retrieving ClientId or ClientSecret from App Settings

I am attempting to use Azure Key Vault from within my ASP.NET MVC Web Application, and I am following these instructions.
My Web.config looks like this (same as in the instructions):
<!-- ClientId and ClientSecret refer to the web application registration with Azure Active Directory -->
<add key="ClientId" value="clientid" />
<add key="ClientSecret" value="clientsecret" />
<!-- SecretUri is the URI for the secret in Azure Key Vault -->
<add key="SecretUri" value="secreturi" />
And my method to obtain the access token looks like this (same as instructions):
//add these using statements
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Threading.Tasks;
using System.Web.Configuration;
//this is an optional property to hold the secret after it is retrieved
public static string EncryptSecret { get; set; }
//the method that will be provided to the KeyVaultClient
public static async Task<string> GetToken(string authority, string resource, string scope)
{
var authContext = new AuthenticationContext(authority);
ClientCredential clientCred = new ClientCredential(WebConfigurationManager.AppSettings["ClientId"],
WebConfigurationManager.AppSettings["ClientSecret"]);
AuthenticationResult result = await authContext.AcquireTokenAsync(resource, clientCred);
if (result == null)
throw new InvalidOperationException("Failed to obtain the JWT token");
return result.AccessToken;
}
I have placed my ClientId, ClientSecret, and SecretUri into my web app's Application Settings, just like the screenshot shows in the instructions. Since I did this, I can expect (from the instructions):
If you have an Azure Web App, you can now add the actual values for the AppSettings in the Azure portal. By doing this, the actual values will not be in the web.config but protected via the Portal where you have separate access control capabilities. These values will be substituted for the values that you entered in your web.config. Make sure that the names are the same.
However, when I run the method above, the value for WebConfigurationManager.AppSettings["ClientId"] resolves to clientid which is the dummy value, and likewise for ClientSecret. My understanding is that the method is supposed to reach out to the web app in the Azure Portal and substitute the values. What am I missing? Why aren't the values being substituted?
Edit: Also, it may be important that I'm using Azure Active Directory B2C instead of Azure Active Directory.
When you run or debug application from your local environment the application picks values from web.config and so you are seeing dummy values on your web page. Your application will pick values from Azure App settings when you deploy your application to Azure. Also, you need to keep Key Name same in web.config as well as in the Azure app setting. Hope this helps.

Azure. Owin OpenId authentication. Added custom claims. AuthorizationCodeReceived is not called

I've almost configured my OpenId owin authentication/authorization in Azure Active Directory. My configuration is the following:
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
CookieName = "AppServiceAuthSession"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = ClientId,
Authority = _authority,
PostLogoutRedirectUri = PostLogoutRedirectUri,
RedirectUri = PostLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = context =>
{
context.HandleResponse();
context.Response.Redirect("/Error?message=" + context.Exception.Message);
return Task.FromResult(0);
},
AuthorizationCodeReceived = async context =>
{
var id = new ClaimsIdentity(context.AuthenticationTicket.Identity.AuthenticationType);
id.AddClaims(context.AuthenticationTicket.Identity.Claims);
var appToken = "MyToken";
id.AddClaim(new Claim("MyTokenKey", appToken));
context.AuthenticationTicket = new AuthenticationTicket
(
new ClaimsIdentity(id.Claims, context.AuthenticationTicket.Identity.AuthenticationType),
context.AuthenticationTicket.Properties
);
}
},
});
But I want to add one more application token (not user token) to claims list to be able to have ability to use this token in any place on my site. Also it's good point for me that I don't need to get this token from my external token provider more then one time per an authentication session.
But place, where I'm going to add my logic (AuthorizationCodeReceived as well as other methods from OpenIdConnectAuthenticationNotifications) is called only when I use my local IIS(run locally), when I try to use azure IIS, this method has not been called at all. In this case my User is authenticated anyway, but this method and the similar methods from OpenIdConnectAuthenticationNotifications(except RedirectToIdentityProvider) are not fired.
I've downloaded the git source code of Katana project and referenced this project to my instead of the official nuget packages to debug its and as I think currently, I've found the reason why it happens. The AuthorizationCodeReceived "event" method is called from OpenIdConnectAuthenticationHandler class in AuthenticateCoreAsync method. But also, the calling of this method is required that the below checking must give the true result:
if (string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase)
&& !string.IsNullOrWhiteSpace(Request.ContentType) // May have media/type; charset=utf-8, allow partial match.
&& Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase)
&& Request.Body.CanRead)
{
//some necessary preparation to call `AuthorizationCodeReceived` event method
}
As we can see, this checking allows only POST requests and I see these POST requests when I run app in local IIS, but I cannot see these POST requests when I deploy my application in azure portal (I've debugged both of options : on local IIS and in azure portal).
As summary from the above, this is the only one difference between these runnings. (Azure IIS doesn't send POST request at all by some reason).Any other methods in Katana project (which I checked) are called in the same way.
Could anybody help with it?
PS Note, I check any changes only after clearing of browser data (cache/history and so on).
The answer is the following:
The authorization in azure portal should be configured as shown above. In case if you chose LogIn with Azure Active Directory, then app services auth takes place outside of your app, and the custom authorization is not triggered.

WebAPI Secured with Azure AD Authentication not working on IIS but working fine on IIS Express using Visual Studio

I am working on a AnguarJS SPA application calling with an Asp.Net WebAPI.
I have registered both the Client as well as the Backend Application on the Azure AD.
My Client/Web Application is registered with the following details:
Sign On URL: http://localhost:93
APP ID URL : http://xyz.onmicrosoft.com/XYZLocalClient
ClientID: 34A721C3-20E4-41D5-9BC1-486A99BF7C26
Reply URL: http://localhost:93
I have given the permissions to other applications (delegated permission) for the client app to access the WebAPI (LocalWebAPI).
My WebAPI has the following setup:
It is using the OWIN Middleware with the startup.cs file as:
public class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
private void ConfigureAuth(IAppBuilder app)
{
var azureADBearerAuthOptions = new
WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings["ida:Tenant"]
};
azureADBearerAuthOptions.TokenValidationParameters =
new System.IdentityModel.Tokens.TokenValidationParameters()
{
ValidAudience =
ConfigurationManager.AppSettings["ida:Audience"]
};
app.UseWindowsAzureActiveDirectoryBearerAuthentication
(azureADBearerAuthOptions);
}
It is registered on the Azure AD with the following parameters:
SIGN-ON URL: http://localhost:93/Api/V1/
APP ID URI: https://xyz.onmicrosoft.com/LocalCognia
Reply URLs: http://localhost:93/Api/V1/*
My Web.Config file is:
<add key="owin:AutomaticAppStartup" value="true"/>
<add key="ida:Tenant" value="xyz.onmicrosoft.com" />
<add key="ida:Audience" value="34A721C3-20E4-41D5-9BC1-486A99BF7C26" />
I have also decorated my controller with the [Authorize] Attribute.
Everything seems to be working fine. I am able to authenticate the user and able to access the resources from the WebAPI when I run my application from the Visual Studio 2015 environment (IIS Express).
But as soon as I deploy my application on the IIS Server, using the same parameters, (expect that the application is now on localhost:8087 and with the reply URL for the client app as: localhost:8087), I am getting error as 401: UnAuthroized user on calling the WebAPI.
I am getting the token in the Headers for the WebAPI call, but still getting the error. Not sure of this behavior. Can someone please help on this?
Please use below code in your ConfigureAuth :
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"]
},
});

Authenticate with Azure Mobile Service from UWP client

I'm struggling with authenticating towards an Azure Mobile Service (.NET backend) via Azure AD. I've been following this tutorial: https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-how-to-configure-active-directory-authentication/
The authentication to Azure AD itself is successful (result.Status == AuthenticationStatus.Success), but I get HTTP 401 at MobileService.LoginAsync.
Mobile Service Azure AD app config
Sign-on URL: https://contososervice.azurewebsites.net
Client ID: c710fe9b-4dd2-406b-ae68-ea5825c2c103
App ID URI: https://contososervice.azurewebsites.net
Reply URL: https://contososervice.azurewebsites.net/.auth/login/aad/callback
Native client Azure AD app config
Client ID: d79fea3f-2357-4797-9be8-48d630f6e1a3
Redirect URIs:
- https://contososervice.azurewebsites.net/.auth/login/done
- ms-app://S-1-15-2-4177921760-2458829842-3328621796-4043898254-238447652-453539330-2174227773
Permission delegated to ContosoService
Azure mobile service authentication config: advanced mode
Client ID: c710fe9b-4dd2-406b-ae68-ea5825c2c103
Issuer URL: https://sts.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47
Mobile service web.config
<add key="ida:Tenant" value="contoso.onmicrosoft.com" />
<add key="ida:Audience" value="https://contososervice.azurewebsites.net" />
Mobile service authentication setup
public void ConfigureAuth(IAppBuilder app)
{
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"]
},
});
}
UWP client
string appIDUri = "https://contososervice.azurewebsites.net";
string clientID = "d79fea3f-2357-4797-9be8-48d630f6e1a3";
AuthenticationResult result = await _authContext.AcquireTokenAsync(
appIDUri,
clientID,
WebAuthenticationBroker.GetCurrentApplicationCallbackUri());
if (result.Status == AuthenticationStatus.Success)
{
IsUserAuthenticated = true;
UserData = result.UserInfo;
success = true;
JObject payload = new JObject();
payload.Add("access_token", result.AccessToken);
var user = await ServiceClient.ServiceClient.MobileService.LoginAsync(
MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory,
payload);
}
I actually managed to solve this.
Remote debugging the Mobile Service via Visual Studio has helped, because Microsoft.Azure.AppService.Authentication tracing showed that:
A browser request posted the token to https://contososervice.azurewebsites.net/.auth/login/aad/callback, which was successfully processed
My client app posted the token to https://contososervice.azurewebsites.net/login/aad, which didn't work.
It took me a while to understand that the URL the tokens are posted to is actually identical to the appIDUri in the code, and because that identifies the resource, too, I had to change the corresponding App ID URI setting in Mobile Service Azure AD app config.
Thus I had to change both appIDUri and App ID URI in Azure AD tohttps://contososervice.azurewebsites.net/.auth/login/aad/callback
and it works now.

Multi-tenant Azure Mobile App service calls failing with 401.71

To preface this, I'm new to Azure programming and Azure AD authentication and I've been following tutorials I've found at various sites (including MS) to get me this far. I'm using Xcode v7.2, ADAL for iOS v1.2.4, Visual Studio 2015 Update 1, and the Azure App Service Tools v2.8.1.
I have an existing native iOS app that I need to be able to authenticate multiple Azure Active Directory instance users through. These users are internal and external (customers who sign up for our services). To that end, I've experimentally implemented the following high level architecture:
Native Client App (iOS / obj-c) -> ADAL iOS library -> (Azure AD authentication) -> Azure Mobile App (service layer)
The iOS app utilizes the ADAL iOS library to acquire an access token which it uses to call authorized Web API services in the Azure Mobile App project.
I'm able to authenticate users from two tenants (an internal Azure AD and an external Azure AD), but only users in the same tenant as the service (internal) are able to call the authenticated APIs. The test user account I used from the external tenant is set up as a Global Admin and I am presented with the appropriate consent view in the native app when authenticating. I can then click through the consent and I receive an access token. When using that token to call a test API however, I get a 401 back. The verbose logs for the Azure Mobile App on the server show the following messages (all URLs below are https, I just don't have the rep to post them as such):
2016-01-12T13:00:55 PID[7972] Verbose Received request: GET MyAzureMobileApp.azurewebsites.net/api/values
2016-01-12T13:00:55 PID[7972] Verbose Downloading OpenID configuration from sts.windows.net/<internal AD GUID>/.well-known/openid-configuration
2016-01-12T13:00:55 PID[7972] Verbose Downloading OpenID issuer keys from login.windows.net/common/discovery/keys
2016-01-12T13:00:56 PID[7972] Warning JWT validation failed: IDX10205: Issuer validation failed. Issuer: 'sts.windows.net/<external AD GUID>/'. Did not match: validationParameters.ValidIssuer: 'sts.windows.net/<internal ad guid>/' or validationParameters.ValidIssuers: 'null'..
2016-01-12T13:00:56 PID[7972] Information Sending response: 401.71 Unauthorized
I've read in several posts that you can disable the token issuer validation in your service by setting the ValidateIssuer parameter in TokenValidationParameters to false. I've tried to do this, but it doesn't seem to have any effect. Here is the code from my Azure Mobile App project:
The startup code:
// Startup.cs
using Microsoft.Owin;
using Owin;
[assembly: OwinStartup(typeof(MyAzureMobileApp.Startup))]
namespace MyAzureMobileApp
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureMobileApp(app);
ConfigureAuth(app);
}
}
}
The code for the MobileApp -- this should be stock, as generated by the Azure Mobile App project template:
// Startup.MobileApp.cs
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data.Entity;
using System.Web.Http;
using Microsoft.Azure.Mobile.Server;
using Microsoft.Azure.Mobile.Server.Authentication;
using Microsoft.Azure.Mobile.Server.Config;
using MyAzureMobileApp.DataObjects;
using MyAzureMobileApp.Models;
using Owin;
namespace MyAzureMobileApp
{
public partial class Startup
{
public static void ConfigureMobileApp(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
new MobileAppConfiguration()
.UseDefaultConfiguration()
.ApplyTo(config);
// Use Entity Framework Code First to create database tables based on your DbContext
Database.SetInitializer(new MobileServiceInitializer());
MobileAppSettingsDictionary settings = config.GetMobileAppSettingsProvider().GetMobileAppSettings();
if (string.IsNullOrEmpty(settings.HostName))
{
app.UseAppServiceAuthentication(new AppServiceAuthenticationOptions
{
// This middleware is intended to be used locally for debugging. By default, HostName will
// only have a value when running in an App Service application.
SigningKey = ConfigurationManager.AppSettings["SigningKey"],
ValidAudiences = new[] { ConfigurationManager.AppSettings["ValidAudience"] },
ValidIssuers = new[] { ConfigurationManager.AppSettings["ValidIssuer"] },
TokenHandler = config.GetAppServiceTokenHandler()
});
}
app.UseWebApi(config);
}
}
public class MobileServiceInitializer : CreateDatabaseIfNotExists<MobileServiceContext>
{
protected override void Seed(MobileServiceContext context)
{
List<TodoItem> todoItems = new List<TodoItem>
{
new TodoItem { Id = Guid.NewGuid().ToString(), Text = "First item", Complete = false },
new TodoItem { Id = Guid.NewGuid().ToString(), Text = "Second item", Complete = false }
};
foreach (TodoItem todoItem in todoItems)
{
context.Set<TodoItem>().Add(todoItem);
}
base.Seed(context);
}
}
}
The authentication startup code:
// Startup.Auth.cs
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IdentityModel.Tokens;
using System.Linq;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.ActiveDirectory;
using Owin;
namespace MyAzureMobileApp
{
public partial class Startup
{
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
AuthenticationMode = AuthenticationMode.Active,
TokenValidationParameters = new TokenValidationParameters()
{
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"],
ValidateIssuer = false
}
});
}
}
}
The service implementation:
using System.Web.Http;
using Microsoft.Azure.Mobile.Server.Config;
namespace MyAzureMobileApp.Controllers
{
// Use the MobileAppController attribute for each ApiController you want to use
// from your mobile clients
[MobileAppController]
// Use the MobileAppController attribute for each ApiController you want to use
// from your mobile clients
[Authorize]
public class ValuesController : ApiController
{
// GET api/values
public string Get()
{
return "GET returned: Hello World!";
}
// POST api/values
public string Post()
{
return "POST returned: Hello World!";
}
}
}
And my appSettings section in web.config:
<appSettings>
<add key="PreserveLoginUrl" value="true" />
<!-- Use these settings for local development. After publishing to your
Mobile App, these settings will be overridden by the values specified
in the portal. -->
<add key="MS_SigningKey" value="Overridden by portal settings" />
<add key="EMA_RuntimeUrl" value="Overridden by portal settings" />
<!-- When using this setting, be sure to add matching Notification Hubs connection
string in the connectionStrings section with the name "MS_NotificationHubConnectionString". -->
<add key="MS_NotificationHubName" value="Overridden by portal settings" />
<add key="ida:ClientId" value="-- MyAzureMobileApp App ID from Azure AD --" />
<add key="ida:Tenant" value="InternalTestAD.onmicrosoft.com" />
<add key="ida:Audience" value="https://InternalTestAD.onmicrosoft.com/MyAzureMobileApp" />
<add key="ida:Password" value="-- password value removed --" />
</appSettings>
I don't see a place to specify valid token issuers except as a property of the TokenValidationParameters collection in WindowsAzureActiveDirectoryBearerAuthenticationOptions.
According to my understanding of the code, I should have issuer validation disabled, but I have tried adding the external Azure AD STS URL here. Unfortunately, it doesn't seem to have any effect.
Does anybody know if this code is getting ignored or overridden for some reason? Is there some other setting I've missed to either disable issuer validation altogether, or specify a list of valid issuers?
I can certainly provide more information as requested, I'm just not sure what else might be relevant.
Thanks!
I believe I have found the cause of my validation logic being ignored. In the setup of my web api site in Azure App Services, I specified the primary tenant issuer URL by populating the Issuer URL textbox in the "Authentication/Authorization" > "Azure Active Directory Settings" blade. It turns out that when you're going to have more than one issuer (as in my multi-tenant scenario) you should leave this field blank.
It makes perfect sense that the JWT will validate against the issuer you provide in that textbox. What is not so intuitive to me is that you should leave it blank when you have more then one issuer. Maybe MS could add that in to the information bubble above it? Either that or provide some mechanism for allowing multiple issuer URLs.
Hopefully this saves someone else some time with this issue.
Just want to point out that if you have configured the authentication, and you had set the primary tenant issuer URL, and then you turned off this type of authentication, the API still reads from it. Clearing this field worked for me, though I never would have thought so, since I was no longer using AD authentication.

Resources