Consuming Id_token from Azure using UseWindowsAzureActiveDirectoryBearerAuthentication - c#

Needing help from anyone.
I have a SPA client application that is talking to a WebApi (.Net 4.52). Both have been developed by my company. It has been working quite fine with tokens that the WebAPI has been generating itself (based on the excellent articles from bitoftech)
We now want to allow authentication with Azure - more specifically, my customer's own azure tenant (which I have no direct control over). To do this on the client-side, we use adal.js to do the back and forth with actually signing the user in, and we use UseWindowsAzureActiveDirectoryBearerAuthentication on the server-side to validate the azure tokens.
The Oauth startup looks like this:
public static void RunAcademyOAuthStartup(IAppBuilder app)
{
if (ConfigurationManager.AppSettings["ida:Enabled"] == "1")
{
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
{
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"],
AuthenticationType = "ExternalAzureSSO"
},
Tenant = ConfigurationManager.AppSettings["ida:Tenant"]
}
);
}
var apiAccessTokenLifeTimeSeconds = 0;
if (!int.TryParse(ConfigurationManager.AppSettings["ApiAccessTokenLifetimeSeconds"], out apiAccessTokenLifeTimeSeconds))
{
apiAccessTokenLifeTimeSeconds = 1800; //set default to 30 minutes
}
var oAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/api/user/authenticate"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(apiAccessTokenLifeTimeSeconds),
Provider = new SimpleAuthorizationServerProvider(),
RefreshTokenProvider = new SimpleRefreshTokenProvider(),
AuthenticationType = "LocalApplication"
};
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
// Token Generation - setup the token authorization server
app.UseOAuthAuthorizationServer(oAuthServerOptions);
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
}
The code above is no different to hundreds of other examples I've been trawling through for days.
This all works fine when I use my free Azure instance (through my company's MSDN) - I can authenticate, the token comes back from azure, webapi can decode it and validate and it continues on.
My problem arises when I try this against my customer's P1 Azure instance - I can authenticate, get a token back, but my webapi does not decode the token. There is no errors logged as far as I can see.
The main thing I can see at the moment is when I get the azure token and decode it using jwt.io, I can see the "x5t" and "kid" values. Based on other information I have found around the 'net, I should be able to get the latest keys from https://login.microsoftonline.com/common/discovery/keys and find either the "x5t" and/or the "kid" values in there....but I can't.
Is this possible? What would cause a token to be generated with keys other than the common ones? Is this a configuration item within the app registration in azure? Do I need to add more settings to the UseWindowsAzureActiveDirectoryBearerAuthentication config in web api?
Tokens are all v1

An answer for anyone who is coming across this thread in the future...probably a rookie mistake :)
What I found in my case was that the admin on the Azure side had registered my application using the "Enterprise Applications" option in their Azure Portal BEFORE editing the manifest,etc in the "App Registrations" area. According to Microsoft Support, it should be done the other way around - register in App Registrations first, then go to Enterprise Applications (if needed). Doing things in that order means that the azure tenant then generates the tokens and signs them with the "common" keys, and not the custom key that a Enterprise App gets.

Related

JIRA OAuth Issues

there
I want to use JIRA API to access data from my company's JIRA instance. The steps of login process on internet browser are:
Type https://mycompanyname.atlassian.net
Direct to login page.
Type my company email address
It re-directly to Microsoft authentication page, click "Continue".
Type password of my Azure AD domain account.
Select a method to verify my identify (I select way of verifying code sent to my bind mobile)
Bingo!
My questions are:
1.How can I finish above steps by C# code?
2.Does my application need the mobile text code verification for every instance?
FYI:
I want to put my application on Azure function as a timer trigger to run some data from JIRA. However, I get stuck at first step-authentication.
I do not know if you have already solved your issue. But from an azure function there is a few steps you will need to take.
In Azure use Managed Identity, you will need this for function to get some details you will save into Keyvault
Have a Keyvault ready cause you gonna need it to get some secrets out of it
Create your Function app and install Atlassian.SDK - can see documentation here
In the dependency injection of your function call
var config = new ConfigurationBuilder()
.AddAzureKeyVault(secretClient, new KeyVaultSecretManager())
.AddEnvironmentVariables()
.Build();
container.Register(p => Options.Create(config.Get<ConfigDetailsModel>()))
.SingleInstance();
In your Jira service use the following to signin
public JiraService(IOptions<ConfigDetailsModel> config)
{
_config = config.Value;
}
var jira = Jira.CreateRestClient(_config.JiraUri, _config.JiraUserName, _config.JiraApiKey);
var result = await jira.Projects.GetProjectsAsync();
Go to https://id.atlassian.com/manage-profile/security/api-tokens and create an API key for your password as plaintext passwords are not supported anymore by Jira.
Save your username, password and url into keyvault, make password expire at some point.
This should work. Unfortunately the OAUTH way, more secure than the basic way is not really geared towards system-to-system way, well not that I could find, as it requires a user input to allow verification code. If you do not mind moving the flow out of function and into a user flow with OAuth then best article I found is here, but you need to copy additional things from his github to get it to work. I got this flow working in Blazor, and tried to get it to work in my Servicebus triggered function.
The only way I could get the Jira stuff to work in function for service-to-service was to use basic Auth either via the Atlassian.SDK or Httpclient
Note SDK makes life somewhat easier than writing own http client stuff
var client = new HttpClient
{
BaseAddress = new Uri(_config.JiraUri)
};
client.DefaultRequestHeaders.Accept.Clear();
var authenticationString = $"{_config.JiraUSerName}:{_config.JiraApiKey}";
var base64EncodedAuthenticationString = Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(authenticationString));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", base64EncodedAuthenticationString);
var project = await client.GetAsync($"rest/agile/1.0/epic/{projectId}");
var content = await project .Content.ReadAsStringAsync();
UPDATE
When you create an API key in Jira for use in code, know that the API key in JIRA is tied to the person who created it.
It should be an Admin account
Create a global break glass account to manage this, you do not want an apikey tied to a user that might leave in a few months
This break glass account should belong to your DevOps team and no one else who can exploit this api key

Web API Identity User with JWT on two API's sharing the same Auth params

I am really struggling to find the answer to this question. I have created a registration WebApi that is working. I can register a user, again roles, login and receive back a JWT. This api will be hosted online to allow users to access my page.
The frontend is a Blazor Web Assembly Application. This also communicates to another API, this API is installed locally on a clients machine, that allows communication to another application.
A lot of these calls to the local api need to be authorized.
This is the part I am really struggling to find an answer for.
How do I get this API to authorize against the JWT that is being given out by the other API. All documentation I have found bundles these features together.
What have I tried?
I have tried added the same auth properties to this local api, but it appears I will also need to provide it access to the database for it to build, which of course isn't ideal and doesn't seem correct.
If someone could give me some pointers, that would be fantastic!
To authenticate the requests with the other API, that API must validate the token with the same secret it was signed with.
So, if you're issuing the token with:
var jwtHandler = new JwtSecurityTokenHandler();
var accessToken = jwtHandler.CreateJwtSecurityToken(
issuer: _environment.ApplicationName,
audience: _environment.ApplicationName,
subject: principal.Identities.First(),
expires: DateTime.Now.AddMinutes(_options.ExpirationMinutes),
signingCredentials: _options.SigningCredentials // <-- key used to sign the token
);
The other service must validate it with the same key:
services.AddAuthentication(/*...*/).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
// ValidateIssuer = false,
// ValidateAudience = false,
ValidateIssuerSigningKey = true, // <--
IssuerSigningKey = jwtOptions.SigningCredentials.Key, // <-- same key
};
});
The only thing you really need to share between the apps is the signing key.
But this isn't ideal if you're dealing with more apps. Would you share the key with all of them? You might, but what happens if you need to reset the key? You'd have to reset it in all apps.
For those cases, a better solution would be to delegate the token signing to a separate service, like Identity Server (or any other OIDC provider), then use & validate the tokens issued by that service in all apps.

MSAL + Azure App Services

I've posted this before, but the thread became pretty extensive and confusing and a resolution was never met. I'm reposting with a clear and concise block of code and my desired outcome.
I'm looking to use client-flow authentication for an Azure App Services backend.
I'd like to use MSAL, to support both Microsoft Accounts (MSA) and AAD accounts. Been stuck on this for weeks with no resolution in sight.
PublicClientApplication myApp = new PublicClientApplication("registered-app-id-in-apps.dev-portal");
string[] scopes = new string[] { "User.Read" };
AuthenticationResult authenticationResult = await myApp.AcquireTokenAsync(scopes);
JObject payload = new JObject();
payload["access_token"] = authenticationResult.AccessToken;
payload["id_token"] = authenticationResult.IdToken;
user = await MobileService.LoginAsync(MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory, payload);
Why doesn't this work?
What do I have to do to get it to work?
Getting a 401 exception, tried with MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory as well as MobileServiceAuthenticationProvider.Microsoftaccount
--App Service Auth Config for Microsoft Account:
ClientID and ClientSecret as it appears in apps.dev.microsoft.com
--App Service Auth Config for AAD:
ClientID as it appears in apps.dev.microsoft.com
Issuer URL: https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration
Client Secret: (Blank)
Been having the same issue, having set up Azure Active Directory authentication on the App Service & attempting to authenticate from a WinForms client using MSAL. Turns out that, as of the time of this writing, Azure App Service does not support AAD V2 (including MSAL). Found the below note here:
At this time, AAD V2 (including MSAL) is not supported for Azure App Services and Azure Functions. Please check back for updates.
So ADAL seems to be the only viable option at the moment, unless you handle the authentication inside your backend code yourself.

Azure AD callback is not going to AuthenticateExternalAsync method

I am using OpenID Connect to connect to Azure ID, I can successfully authenticate in Azure and get the request coming back to the redirect uri specified in OpenID Azure AD Configuration.
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
AuthenticationType = " TEST",
Caption = "Test Azure AD",
SignInAsAuthenticationType = signInAsType,
ClientId = "<client ID>",
Authority = "https://sts.windows.net/<tenantId>",
ResponseType = OpenIdConnectResponseTypes.CodeIdToken,
RedirectUri = "https://localhost:44392/External/Login", This is another webapi project, not identityserver host.
AuthenticationMode = AuthenticationMode.Passive,
});
After succesful authentication it is redirecting back to https://localhost:44392/External/Login with Code, IdToken.
Questions :
Does it not stop at AuthenticateExternalAsync method on redirection unlike google-signin ?
Do i have to decode IdToken JWT to get user claims?
In the redirection method, how do i generate Access Token from IdSrv3 to authorize other webapis ?
Can a user have both Local Login and Multiple External logins ( Azure AD, Google etc ). In this case how does SSO works with IDsrv3 ?
Is there any IdSrv3 sample with External logins implemented ? Preferably Azure AD ?
I've just struggled through this process, so I'll attempt to answer as best I can to help you/others. Forgive me if I misunderstand your question.
AuthenticateExternalAsync should be called, but you need to have AzureAd return to the IDS (Identity Server) rather than to your App. Your flow should look something like: app -> IDS -> AzureAd -> IDS (AuthenticateExternalAsync) -> App.
In AuthenticateExternalAsync you get the ExternalAuthenticationContext.ExternalIdentity, which contains the claims - no need to decode the JWT token.
IDS handles this once you return a successful AuthenticatedResult in AuthenticateExternalAsync, something like context.AuthenticateResult = new AuthenticateResult("UserId", name, claims);
Yes. You can force the method of logging in as described for SSO purposes, otherwise I imagine IDS would handle it post first-login.
I found this helpful (runs through setup of IDS and AzureAd), but it does use the old Azure Portal rather than the new one. They don't seem to have any samples in their gallery.
Hope that helps a bit :)

ASP.Net identity: Difference between UseOAuthBearerTokens and UseCookieAuthentication?

The ASP.NET team has shipped new samples showing how to use the identity packages. They are contained in the following nuget package: Microsoft Asp.Net Identity Samples
The samples are very helpful, but there have been loads of changes from how things were done originally in the templates that shipped.
My specific question: In the original SPA template, there was the following code:
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId, UserManagerFactory),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = true
};
...
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
But in the new samples in the nuget package, that code is gone, and in its place is this code:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(validateInterval: TimeSpan.FromMinutes(30), regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
Can anyone help me understand the difference between app.UseOAuthBearerTokens and app.UseCookieAuthentication (and why this change was made)? They both seem to allow the application to behave in the same way and I could use some clarification on this change.
Thanks...
-Ben
OAuth is used for API security, IN API you would not use cookies and this is essentially stateless. However on say a normal MVC or forms website, you would use a normal session cookie. unless you are creating an API I would not worry about OAuth, and just go with the traditional cookie based auth.
If you want to create an API, then you need to, and I would say MUST DO OAuth for your validation. You would then send a token with your request, post, put, delete. This token is decoded backend by the handlers to reveal permissions, User ID etc
Thought it would be best to extend this and explain the problems, and why OAuth solves it.
Usually an api would be on a separate domain to the UI, be that an APP, Website etc. If you did manage to be given a cookie from an API ( for example facebook.com ) You would only be able to use this cookie on facebook. But your website would be www.myblog.com. There are settings in Ajax to enable the passing of cookies with ajax request, however the domain must be the same, and this is still rather sketchy.
So Oauth Is born, essentially creating what could be best described as a string based cookie, that can be stored however you like, so long as it is passed back, with your requests, within the request headers.
You can in browser applications use javascript to create a cookie, and save your token within this cookie. This would allow you to take advantage of the persistent storage. However it is probably better to use available local storage. So for a browser based app, this would be LocalStorage API, for desktop apps you could use temp storage, local db, etc. and phone apps will have something similar.

Categories

Resources