Custom OAuth2 server returns 401 - Unathorized - c#-4.0

I'm trying to do custom OAuth2 authorization server that will support Resource Owner Password Credentials flow. The authorization server is an WebAPI application hosted in IIS7.5.
I have configured startup class where I register custom OAuthServerProvider (AtcAuthorizationServerProvider).
[assembly: OwinStartup(typeof(ATC.WebApi.AuthorizationServer.Startup))]
namespace ATC.WebApi.AuthorizationServer
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureOAuth(app);
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
app.UseWebApi(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
Provider = new AtcAuthorizationServerProvider(),
RefreshTokenProvider = new AtcRefreshTokenProvider(),
AuthenticationMode = AuthenticationMode.Passive
};
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions(){});
}
}
}
In my custom provider class, I override ValidateClientAuthentication() function where I accept both client credentials receiving ways (in Body and in Authorization header).
public class AtcAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string clientId = string.Empty;
string clientSecret = string.Empty;
// get client credentials from header or from body
if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
{
context.TryGetFormCredentials(out clientId, out clientSecret);
}
//rest of code
Everything works fine when I send client_id and client_secret in body.
POST /ATC.WebApi.AuthorizationServer/token HTTP/1.1
Host: localhost
Accept: application/json
Content-Type: application/x-www-form-urlencoded
Cache-Control: no-cache
grant_type=password&password=123456&username=myUser&client_id=myClient&client_secret=123%40abc
I get access token successfully.
{
"access_token": "3Fk_Ps10i45uL0zeCzIpvEh2WHKE8iJVNtKJ2XGWcQWXsT9jllKf...",
"token_type": "bearer",
"expires_in": 1799,
"refresh_token": "4c1097d17dd14df5ac1c5842e089a88e",
"as:client_id": "myClient"
}
However, if I use DotNetOpenAuth.OAuth2.WebServerClient which passes client_id and client_secret in Authorization header I will recieve 401.1 - Unauthorized HTTP response. I have found out that the ValidateClientAuthentication() is not fired.
Request than looks like this:
POST /ATC.WebApi.AuthorizationServer/token HTTP/1.1
Host: localhost
Accept: application/json
Content-Type: application/x-www-form-urlencoded
Authorization: Basic C16b34lUjEyM0BhYmM=
Cache-Control: no-cache
grant_type=password&password=123456&username=myUser
The question is how to persuade probably the OWIN middle-ware firing my custom Provider in this case?

Well, I finally found out where is the trouble. There was Basic authentication allowed in my IIS, so IIS got the request and tried to Authenticate User which failed and IIS returned 401 Unauthorized immediately. So my OWIN middleware even did not receive the request to processing.

Related

Mobile app performing an HTTP Post to Azure Function using Bearer Token and Function Key returns Unauthorized

I'm using a mobile app and am receiving an Unauthorized response when attempting to post to an Azure Function and providing a function key.
Error:
StatusCode: 401, ReasonPhrase: 'Unauthorized'
Code:
let postToAsync (baseAddress:string) (resource:string) (payload:Object) =
async {
let tokenSource = new CancellationTokenSource(TimeSpan(0,0,30));
let token = tokenSource.Token;
try
let tokens = resource.Split("?code=")
let functionKey = tokens.[1]
use client = httpClient baseAddress
client.DefaultRequestHeaders.Add("x-functions-key", functionKey)
client.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue("application/json"))
let json = JsonConvert.SerializeObject(payload)
let content = new StringContent(json, Encoding.UTF8, "application/json")
let! response = client.PostAsync(resource.Replace($"?code={functionKey}",""), content, token) |> Async.AwaitTask
Debug.WriteLine $"\n\n{baseAddress}{resource}\nSuccess: {response.IsSuccessStatusCode}\n\n"
return response
with ex -> ...
} |> Async.StartAsTask
Note:
My Azure Function's AuthorizationLevel is set to Function.
I can call the function successfully when I publish it manually from Visual Studio.
However, when I deploy the function using Pulumi, I receive an Unauthorized response. I believe this is because Pulumi constrains me to add access policies for each Function App.
Versioning:
<TargetFramework>net6.0</TargetFramework>
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
oauth2/v2.0:
I think the following link provides a clue to why I'm observing the issue. However, I still don't know how to resolve it.
Connectivity
I launched Log Stream and observed that the URL is correct:
Access Control:
Please note that the difference between the Function App that I created without using Pulumi, which lets me post successfully, versus the Function App that was generated using Pulumi, is an Access Policy per Function App with Pulumi.
public static class AccessPolicies
{
public static void Build(string policyName, string functionName, Dictionary<string, CustomResource> registry)
{
var resourceGroup = registry[nameof(ResourceGroup)] as ResourceGroup;
var keyVault = registry[nameof(KeyVault)] as KeyVault;
var functionApp = registry[functionName] as FunctionApp;
var result = new AccessPolicy(policyName, new AccessPolicyArgs {
KeyVaultId = keyVault.Id,
TenantId = TenantId.Value,
ObjectId = functionApp.Identity.Apply(v => v.PrincipalId ?? "11111111-1111-1111-1111-111111111111"),
KeyPermissions = new[] { "Get", },
SecretPermissions = new[] { "Get", },
});
registry.Add($"{policyName}-{functionName}", result);
}
}
}
I tried to reproduce the same in my environment via Postman and got below results:
I have one function app with http function named srifunction like below:
I generated one bearer token with same scope as you like below:
POST https://login.microsoftonline.com/<tenantID>/oauth2/v2.0/token
grant_type:client_credentials
client_id: <appID>
client_secret: <secret_value>
scope: https://management.azure.com/.default
Response:
When I used the above token to call function, I got 401 Unauthorized error same as you like below:
POST https://<funcappName>.azurewebsites.net/api/<function_name>
Authorization: Bearer <token>
If you pass function key in token value, you will still get 401 Unauthorized error like below:
POST https://<funcappName>.azurewebsites.net/api/<function_name>
Authorization: Bearer <function key>
To call function using function key, you need to include key value
in x-functions-key header instead of Bearer token.
When I included the above header, I am able to call the function successfully like below:
POST https://<funcappName>.azurewebsites.net/api/<function_name>
x-functions-key: <function key>

Application showing CORS error after sometime (AZURE AD)

I am using Spring Boot + Azure AD + angular9 and using azure ad default provided api to get access to my application.
After launching application Microsoft login appear -> verify creds -> show me my landing page. All api works fine.
But after sometime (almost 5-6 mins) all api start failing.
It showing CORS ERROR in browser console.
Access to XMLHttpRequest at 'https://login.microsoftonline.com/common/oauth2/authorize?response_type=code
&client_id=<client_id>
&scope=openid%20https://graph.microsoft.com/user.read&state=lZfrMwK0nx5kQDjO56DJsMTFhcuvDmU5o0-ZCvTPvI4%3D
&redirect_uri=https://{abc}.com/login/oauth2/code/azure&nonce=CCGwfpb4klAiPtEApCs3nS8ICod0-htdHWvBATNYfXs'
(redirected from 'https://{abc}.com/api/supplier/add') from origin
'https://{abc}.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
for every api onwards it is showing CORSS ERROR
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://login.microsoftonline.com/common/oauth2/authorize?
response_type=code&client_id=<client_id>
&scope=openid%20https://graph.microsoft.com/user.read
&state=LAJfYfn9XFOISAHO4Cq4iA5_Dkya3CFDXKgQmQVfpxg%3D
&redirect_uri=https://{abc}.com/login/oauth2/code/azure
&nonce=IBF8nVnrWhH-SY9VpNxouZcxn_6JZEo3J_d-JBHTAK4.
(Reason: CORS header ‘Access-Control-Allow-Origin’ missing)
Here is my Web-Security-config
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${logout.url}")
String logoutUrl;
#Value("${redirect.url}")
String redirectUrl;
#Autowired
private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService;
#Override
protected void configure(HttpSecurity http) throws Exception {
String logoffUrl = logoutUrl+"?post_logout_redirect_uri="+URLEncoder.encode(redirectUrl, "UTF-8");
http
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2Login()
.userInfoEndpoint()
.oidcUserService(oidcUserService);
http.logout().logoutUrl("/api/logout")
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
.logoutSuccessUrl(logoffUrl) ;
}
}
WebMvcConfig
#Configuration
public class WebMvcConfig implements WebMvcConfigurer{
#Value("${spring.allowed.origin}")
public String allowedOrigin;
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins(allowedOrigin)
.allowedMethods("GET", "POST")
.allowCredentials(true)
.maxAge(3600);
}
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
urlPathHelper.setUrlDecode(false);
configurer.setUrlPathHelper(urlPathHelper);
}
}
From angular side I am sending header like below
headers = new HttpHeaders({
'Content-Type': 'application/json',
'Access-Control-Allow-Origin' : '*',
'Access-Control-Allow-Credentials': 'true',
});
Please help.. thanks
I implemented SPA using angular msal service.
I use loginPopUp method to get login pop up provided by Microsoft. It solve my ADFS security issue also.
Here I am not using any accessTokenSilent / accessTokenRedirect method to get access token after the previous token expire.
I added CSP header in my nginx.conf file (web server conf file), so after the token expire it automatically providing me new access token on click of any api.
header for CSP added in nginx.conf file
add_header Content-Security-Policy "frame-ancestors 'self';" always;

Failed to get AccessToken via authorization code using MSAL 1.1.0-preview in asp.net core

I followed official steps as below to try the scenario "web app calling a Web API in Azure Ad B2C", the only difference is I am using Asp.Net core. I am using AuthorizationCode to get the access token, but it always returns with id token and NULL access token.
Create an Azure AD B2C tenant.
Register a web api.
Register a web app.
Set up policies.
Grant the web app permissions to use the web api.
My code:
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
AuthenticationScheme = OpenIdConnectDefaults.AuthenticationScheme,
AutomaticChallenge = true,
ClientId = aadB2cSettings.ClientId,
MetadataAddress = $"{aadB2cSettings.Instance}{aadB2cSettings.Tenant}/v2.0/.well-known/openid-configuration?p={aadB2cSettings.B2cSignUpOrSignInPolicy}",
PostLogoutRedirectUri = aadB2cSettings.RedirectUrl,
ResponseType = OpenIdConnectResponseType.CodeIdToken,
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name"
},
Events = new OpenIdConnectEvents
{
OnAuthorizationCodeReceived = async context =>
{
var authCode = context.TokenEndpointRequest.Code;
var b2cAuthority = $"{aadB2cSettings.Instance}tfp/{aadB2cSettings.Tenant}/{aadB2cSettings.B2cSignUpOrSignInPolicy}/v2.0/.well-known/openid-configuration";
var cca = new ConfidentialClientApplication(
aadB2cSettings.ClientId,
b2cAuthority,
aadB2cSettings.RedirectUrl,
new ClientCredential(aadB2cSettings.ClientSecret),
new TokenCache(),
null);
try
{
var authResult = await cca.AcquireTokenByAuthorizationCodeAsync(authCode, new[] { "https://hulab2c.onmicrosoft.com/b2cdemo/all" });
context.HandleCodeRedemption(authResult.AccessToken, authResult.IdToken);
}
catch (Exception ex)
{
throw ex;
}
}
},
Used fiddler to capture the request, it is:
POST
https://login.microsoftonline.com/hulab2c.onmicrosoft.com/oauth2/v2.0/token?p=b2c_1_signuporsignin
HTTP/1.1
Request Body:
client_id=1ff91f47-08ee-4973-83f4-379ad7e0679c&client_info=1&client_secret=......&scope=https%3A%2F%2Fhulab2c.onmicrosoft.com%2Fb2cdemo%2Fall+offline_access+openid+profile&grant_type=authorization_code&code=......&redirect_uri=https%3A%2F%2Flocalhost%3A44383%2F
Return:
{"id_token":"......","token_type":"Bearer","not_before":1494494423,"client_info":"......","scope":""}
So only id token, no access token. But we should get access token here, right?
Finally found out my failure reason: the request to get AuthorizationCode doesn't contain the target scope. Reflect in code, for OpenIdConnectOption in aspnetcore, the Scope parameter is readonly and its default value is "opened profile".
Scope is readonly in OpenIdConnectOption
So the default authorization code request sent is:
GET
https://login.microsoftonline.com/hulab2c.onmicrosoft.com/oauth2/v2.0/authorize?p=b2c_1_signuporsignin&client_id=7f865ca0-271e-4f27-be21-6f0072fe3ad7&redirect_uri=https%3A%2F%2Flocalhost%3A44355%2Fsignin-oidc&response_type=code%20id_token&scope=openid%20profile&response_mode=form_post&nonce=......
HTTP/1.1
Thus, using this authorization code in response to get token, even we set right scope in the token request, we still can't get the access code but only id token, because the provide authorization code is only for "openid profile".
To fix this, we need to add target web api scope into the authorization code as well. Here is the how-to-fix code:
Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProvider = context =>
{
context.ProtocolMessage.Scope += $" offline_access {myapiscope}";
return Task.FromResult(0);
},
......
}
In AspNet, we don't need to do this because its scope is not readonly as aspnetcore and can be set directly:
new OpenIdConnectAuthenticationOptions
{
......
Scope = $"openid profile offline_access {ReadTasksScope} {WriteTasksScope}"
}
https://github.com/Azure-Samples/active-directory-b2c-dotnet-webapp-and-webapi/issues/4 Microsoft have reproduced the issue and working on fix

Authenticate Web App against API App using Azure AD bearer token

I'm attempting to access an API App I have hosted on Azure and secured with Azure AD.
For the API App I've set App Service Authentication = Azure Active Directory "Express" management mode.
In the "classic" portal I've created a couple of applications under AD. One for the API App and another for the Web App. And for the Web App I've added an entry under "permissions to other applications" for the API App (though I'm not sure I need this as "user assignment required to access app" is off for the API App). I've also generated a key for the Web App.
Following the example code given here - https://github.com/Azure-Samples/active-directory-dotnet-webapp-webapi-oauth2-appidentity ...
I can successfully obtain a bearer token using the following code:
private static string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
private static string tenant = ConfigurationManager.AppSettings["ida:Tenant"];
private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
private static string appKey = ConfigurationManager.AppSettings["ida:AppKey"];
static string authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
private static string ApiId = ConfigurationManager.AppSettings["ApiId"];
private static AuthenticationContext authContext = new AuthenticationContext(authority);
private static ClientCredential clientCredential = new ClientCredential(clientId, appKey);
...
AuthenticationResult result = null;
int retryCount = 0;
bool retry = false;
do
{
retry = false;
try
{
// ADAL includes an in memory cache, so this call will only send a message to the server if the cached token is expired.
result = await authContext.AcquireTokenAsync(ApiId, clientCredential);
}
catch (AdalException ex)
{
if (ex.ErrorCode == "temporarily_unavailable")
{
retry = true;
retryCount++;
Thread.Sleep(3000);
}
}
} while ((retry == true) && (retryCount < 3));
if (result == null)
return Request.CreateResponse(HttpStatusCode.InternalServerError, "Could not authenticate against API.");
But when I use the bearer token with with the request from the Web App to the API App I always get a 401 unauthorized response:
StatusCode: 401, ReasonPhrase: 'Unauthorized', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Date: Wed, 13 Jul 2016 08:43:09 GMT
Server: Microsoft-IIS/8.0
WWW-Authenticate: Bearer realm="MY-API-APP-ID-IS-HERE"
X-Powered-By: ASP.NET
Content-Length: 58
Content-Type: text/html
}
This is the code I'm using to make the request that's failing with a 401:
var apiUri = new Uri(ConfigurationManager.AppSettings["ApiUrl"] + "/api/MethodImCalling");
var client = new RestClient(apiUri.GetLeftPart(UriPartial.Authority));
var request = new RestRequest(apiUri, Method.GET);
request.AddHeader("Authorization", "Bearer " + result.AccessToken);
request.AddParameter("something", somevalue);
var response = client.Execute(request);
if (response.StatusCode != HttpStatusCode.OK)
return Request.CreateResponse(response.StatusCode); // Relay non-successful response
Any ideas what I might be doing wrong or am missing? Thanks in advance!
I already have Logic App in Azure accessing the API App without issue, but I note that the authentication credentials in the logic app json include an "audience" parameter. The code above does not use an "audience" so could this be the missing part of the puzzle, and if so, how do I add it?
Screenshot showing how Web App has been configured to access API App:
The reason you are getting a 401 response is that you've only granted your application Delegated Permissions, yet you are using the client credentials flow which requires Application Permissions.
You can either change your code to use the Authorization Code flow or grant application permissions from your web app to your web API.
To use the Authorization Code flow you'd need to change your code to use AcquireTokenByAuthorizationCodeAsync instead.
You can find more information about these two different approaches here:
https://azure.microsoft.com/en-us/documentation/articles/active-directory-authentication-scenarios/#web-application-to-web-api

Unable to authorize at Azure mobile app services using MobileServiceClient

I’ve been trying to implement Azure Active Directory authentication for my Web app at Mobile app.
Mobile app has been configured as described in this tutorial (used Alternative method).
My Azure Active Directory app settings:
SIGN-ON URL: https:// <mymobileappname>.azurewebsites.net/
App ID URI: https:// <mymobileappname>.azurewebsites.net/
Reply URL: https:// <mymobileappname>.azurewebsites.net/.auth/login/aad/callback
Also I’ve passed ClientID(b6da4c72-xxxx-xxxx-xxxx-e20d561b7906) and entityID(https:// sts.windows.net/e052874c-xxxx-xxxx-xxxx-afd774687ee8/) to mobile app authentication settings in the Azure portal and switched it ON.
At my Web app I do folowing:
Take token from Azure Active Directory
public string GetAADToken()
{
string clientID = "b6da4c72-xxxx-xxxx-xxxx-e20d561b7906";
string authority = "https://login.windows.net/<mytenant>";
string resourceURI = "https://<mymobileappname>.azurewebsites.net/";
var appKey = <mysecretvalidkeytakenfromazureactivedirecoryapp>";
var authenticationContext= new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext(authority);
var clientCredential = new ClientCredential(clientID, appKey);
var authenticationResult = authenticationContext.AcquireToken(resourceURI, clientCredential);
return authenticationResult.AccessToken;
}
Authenticate with AAD token at Mobile app using MobileServiceClient(v2.0.1)
MobileServiceClient client = new MobileServiceClient("https://<mymobileappname>.azurewebsites.net/");
var token = new JObject();
token["access_token"] = GetAADToken();
var res = client.LoginAsync("aad", token).Result;
This code sends authentication request to my mobile app
{Method: POST, RequestUri: 'https://<mymobileappname>/.auth/login/aad', Version: 1.1, Headers:
{
X-ZUMO-INSTALLATION-ID: 904579fa-xxxx-xxxx-xxxx-02efc7ba2937
Accept: application/json
User-Agent: ZUMO/2.0
User-Agent: (lang=Managed; os=Windows; os_version=6.2.0.9200; arch=Win32NT; version=2.0.31217.0)
X-ZUMO-VERSION: ZUMO/2.0 (lang=Managed; os=Windows; os_version=6.2.0.9200; arch=Win32NT; version=2.0.31217.0)
Content-Type: application/json; charset=utf-8
Content-Length: 1129
}}
In the POST-body sends json with AAD token.
The same solution was used for azure mobile services and works fine, but for Mobile app service I always get: 401 'Unauthorized'.
What am I doing wrong?

Resources