Blazor: missing JWT token with Safari (macOS and iOS) - asp.net-core

I have a Blazor application with NET6. The authentication is via Identity Server (Skoruba Duende). I created this repo here with a basic project without CSS).
When the application calls the API, I add the user token in the Header.
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
To retrieve the user token, I have a function that read the token from the cache or from IAccessTokenProvider.
IAccessTokenProvider _accessToken;
private async Task<AccessToken> RequestUserToken()
{
try
{
var tokenResult = await _accessToken.RequestAccessToken();
tokenResult.TryGetToken(out var token);
Console.WriteLine(token);
return token;
}
catch (AccessTokenNotAvailableException aex)
{
throw new ApplicationException($"Exception {aex}");
}
catch (Exception ex)
{
throw new ApplicationException(
$"AccessTokenNotAvailable Exception Exception {ex}");
}
}
The application sends successfully requests to the API from Windows and Android. When I run the application on macOS and iOS devices, the token is not found and then there is an error.
To debug this issue, I run the web application on a Mac. On the Mac, the application is working fine with Microsoft Edge and Chrome but not with Safari.
On iPhone and iPad, this issue is present on Safari and Chrome. It works fine on Microsoft Edge.
Add more details
I'm creating application with Blazor in NET6. To call the API via an Azure API Management, I have to pass a token in the request adding
request.Headers.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
In this request, I have to pass the token that comes from an Identity Server. So, first, the user has to authenticate to the identity server and then receives the JWT token.
The configuration is
builder.Services.AddOidcAuthentication(options =>
{
builder.Configuration.Bind("oidc", options.ProviderOptions);
options.UserOptions.RoleClaim = "role";
})
.AddAccountClaimsPrincipalFactory<MultipleRoleClaimsPrincipalFactory<RemoteUserAccount>>();
builder.Services.AddAuthorizationCore();
Also, to apply the security, I add in the Program.cs this lines
app.Use((context, next) =>
{
context.Response.GetTypedHeaders().CacheControl =
new Microsoft.Net.Http.Headers.CacheControlHeaderValue()
{
MustRevalidate = true,
NoCache = true,
NoStore = true,
};
string oidcAuthority = builder.Configuration.GetValue(typeof(string), "oidc:Authority").ToString();
string mainUrl = "https://test.mywebsite.com/";
#if DEBUG
mainUrl = "https://localhost:7040";
#endif
context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
context.Response.Headers.Add("Content-Security-Policy",
$"default-src 'self' {mainUrl} {oidcAuthority} " +
"https://code.cdn.mozilla.net " +
#if DEBUG
"https://dc.services.visualstudio.com " +
#endif
"https://api.myapi.com " +
"'unsafe-inline' 'unsafe-eval'; " +
$"script-src 'self' {mainUrl} 'unsafe-inline' 'unsafe-eval'; " +
$"connect-src 'self' {oidcAuthority} https://code.cdn.mozilla.net https://api.myapi.com;" +
$"img-src 'self' {mainUrl} data:; " +
$"style-src 'unsafe-inline' {mainUrl} " +
"https://code.cdn.mozilla.net " +
";" +
"base-uri 'self'; " +
"form-action 'self'; " +
"frame-ancestors 'self';");
context.Response.Headers.Add("Referrer-Policy", "same-origin");
context.Response.Headers.Add("Permissions-Policy", "geolocation=(), microphone=()");
context.Response.Headers.Add("X-XSS-Protection", "1; mode=block");
context.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN");
context.Response.Headers.Add("SameSite", "Strict");
return next.Invoke();
});
So, after that I can read the JWT token with this code:
IAccessTokenProvider _accessToken;
private async Task<AccessToken> RequestUserToken()
{
try
{
var tokenResult = await _accessToken.RequestAccessToken();
tokenResult.TryGetToken(out var token);
Console.WriteLine(token);
return token;
}
catch (AccessTokenNotAvailableException aex)
{
throw new ApplicationException($"Exception {aex}");
}
catch (Exception ex)
{
throw new ApplicationException(
$"AccessTokenNotAvailable Exception Exception {ex}");
}
}
The application is working on Windows for any browser. On iOS and macOS I can read the token if I use Safari. It is working if I use Microsoft Edge and Chrome.
I have just created this repo here (sorry the project is very basic without CSS).
When you run the application, click on Log in. Then, you will redirect to the Identity Server when you can use the following credentials:
Username: MSTest
Password: MSTest!2023
Then, you can see a new link called Simulation. The Simulation calls the API using the user token.
For your convenient, I deployed the test website here
When you run the app on Windows with Edge, for example, you can see that the call to the API is working.
But when you run the website on an iPhone you get an error.
Microsoft answer
I opened an issue on GitHub under aspnetcore.
Safari Intelligent Tracking Prevention is blocking the cookie from
being sent on the hidden iframe, which is why Surayya's suggestion
works, as it disables that mechanism.
On your app, you are failing to check for the result of
AccessTokenResult if you do, you'll see that it has an error status on
it, hence why TryGetToken returns false. This is all expected so far,
and the way to deal with it, is by performing the authentication flow
interactively.
I am not sure what provider you are using, but the way this works in
AAD is that the flow is code+PKCE and a refresh_token with a duration
of 24h is emitted, which the app then uses to refresh the access
tokens when needed.
This is also why our built-in handler throws an exception when it
can't provision the token, so that you can perform an interactive flow
and provision the token that way.
At this point, I don't know how to secure the APIs via Identity Server with a method that works. Can you recommend/advice me how to do it?

Related

AzureAD, Client confidential app calling webapi with a custom Application ID URI, returns 401

I'm trying to develop an API which can be called from different web apps.
If I call the api with a client confidential app, using the default scope (api://[APIclientId]/.default), everything works.
But If I specify a custom Application ID URI for the API app registration (like: api://myapi.iss.it), and I set the scope to api://myapi.iss.it/.default, I get HTTP401 from the webapp.
This is the method to retrieve the token for the webapp to call the api:
private async Task PrepareAuthenticatedClient()
{
IConfidentialClientApplication app;
string AURY = String.Format(CultureInfo.InvariantCulture, _config["AzureAd:Instance"] + "{0}", _config["AzureAd:TenantId"]);
app = ConfidentialClientApplicationBuilder.Create(_config["AzureAd:ClientId"])
.WithClientSecret(_config["AzureAd:ClientSecret"])
.WithAuthority(new Uri(AURY))
.Build();
var accessToken = await app.AcquireTokenForClient(new string[] { _config["API:scope"] }).ExecuteAsync();
Console.WriteLine("token: " + accessToken.AccessToken);
//var accessToken = await _tokenAcquisition.GetAccessTokenForAppAsync(_TodoListScope);
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.AccessToken);
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
I notice that the Audience is still api://[APIclientId] in the token, even if I set the api:scope to api://myapi.iss.it/.default
Is it correct?
any idea what could be the problem?
I got the solution on the Microsoft Q&A platform.
Basically, I didn't specifiy the Audience in the API application, so by default it was "api://[APIclientId]".
When the API was verifing the token of the app (where the aud was api://myapi.iss.it), the exception "Microsoft.IdentityModel.Tokens.SecurityTokenInvalidAudienceException" was raised, and the API returned 401.
If you have the same problem, and you are using the Microsoft.Indentity.Web library, specifying the Audience in the appsetting.json may be enough.

Windows authentication fail with "401 Unauthorized"

I have a MVC client accessing a Web API protected by IDS4. They all run on my local machine and hosted by IIS. The app works fine when using local identity for authentication. But when I try to use Windows authentication, I keep getting "401 Unauthorized" error from the dev tool and the login box keeps coming back to the browser.
Here is the Windows Authentication IIS setting
and enabled providers
It's almost like that the user ID or password was wrong, but that's nearly impossible because that's the domain user ID and password I use for logging into the system all the time. Besides, according to my reading, Windows Authentication is supposed to be "automatic", which means I will be authenticated silently without a login box in the first place.
Update
I enabled the IIS request tracing and here is the result from the log:
As you can see from the trace log item #29, the authentication (with the user ID I typed in, "DOM\Jack.Backer") was successful. However, some authorization item (#48) failed after that. And here is the detail of the failed item:
What's interesting is that the ErrorCode says that the operation (whatever it is) completed successfully, but still I received a warning with a HttpStatus=401 and a HttpReason=Unauthorized. Apparently, this is what failed my Windows Authentication. But what is this authorization about and how do I fix it?
In case anyone interested - I finally figured this one out. It is because the code that I downloaded from IndentityServer4's quickstart site in late 2020 doesn't have some of the important pieces needed for Windows authentication. Here is what I had to add to the Challenge function of the ExternalController class
and here is the ProcessWindowsLoginAsync function
private async Task<IActionResult> ProcessWindowsLoginAsync(string returnUrl)
{
var result = await HttpContext.AuthenticateAsync(AccountOptions.WindowsAuthenticationSchemeName);
if (result?.Principal is WindowsPrincipal wp)
{
var props = new AuthenticationProperties()
{
RedirectUri = Url.Action(nameof(Callback)),
Items =
{
{ "returnUrl", returnUrl },
{ "scheme", AccountOptions.WindowsAuthenticationSchemeName },
}
};
var id = new ClaimsIdentity(AccountOptions.WindowsAuthenticationSchemeName);
id.AddClaim(new Claim(JwtClaimTypes.Subject, wp.Identity.Name));
id.AddClaim(new Claim(JwtClaimTypes.Name, wp.Identity.Name));
if (AccountOptions.IncludeWindowsGroups)
{
var wi = wp.Identity as WindowsIdentity;
var groups = wi.Groups.Translate(typeof(NTAccount));
var roles = groups.Select(x => new Claim(JwtClaimTypes.Role, x.Value));
id.AddClaims(roles);
}
await HttpContext.SignInAsync(IdentityConstants.ExternalScheme, new ClaimsPrincipal(id), props);
return Redirect(props.RedirectUri);
}
else
{
return Challenge(AccountOptions.WindowsAuthenticationSchemeName);
}
}
Now my windows authentication works with no issues.

Authentication Service throws Procedure invocation error post upgrade from MFP 6.3 to MFP 7.1

Authentication adapter throwing "Procedure invocation error" sometimes. Tried clearing cache and cookies but still the same. So we tried to login from different system for same user and it works. This is quite confusing as once we try with different ID in browser where issue occurred, it works and then it works with Member ID which has issue as well. Auth required is not coming in response when issue occurs.
we have tried to look into logs and found WorklightAuthenticationException from Authentication Adapter while trying security test procedure.
Authentication Adapter code:
var result = WL.Server.invokeHttp(input);
WL.Logger.info("Authentication service : " + JSON.stringify(result));
authResponse = prepareJSONResponse(result,channelId);
WL.Logger.info('Formatted response -> ' + JSON.stringify(authResponse));
if(result.isSuccessful == false){
WL.Logger.info("Error: " + result.errorMessage);
return onAuthRequired(null, "Error in connecting to server. Please try again later.");
}
if(typeof authResponse.errorMessage != 'undefined'){
WL.Logger.info("Error is defined" +authResponse.errorMessage);
return onAuthRequired(null, authResponse);
}
WL.Logger.info("Authentication service success: " + JSON.stringify(result));
WL.Logger.info("userIdentity Parameters: " + inputParams.CorpId);
var userIdentity = {
userId: inputParams.CorpId,
displayName: inputParams.CorpId,
attributes: {
foo: "bar"
}
};
WL.Logger.info("userIdentity::"+JSON.stringify(userIdentity));
WL.Server.setActiveUser("SingleStepAuthRealm", userIdentity);
return {
authRequired: false
};
It is happening due to the requests going from one node to another node. Handled it in Load balancer to send requests to specific node based on cookies and post that it works fine.
The description mentions about clearing cache and cookies and using browser.
Browser based environments are not supported in session independent mode. These work only in session dependent mode. As such, it is imperative that session based affinity be enabled to ensure the requests land in the same JVM for authentication state to be preserved.
More details can be found here : Session-independent mode

Adal.js does not get tokens for external api endpoint resource

I'm trying out adal.js with an Angular SPA (Single Page Application) web site that gets data from an external Web API site (different domain). Authentication against the SPA was easy with adal.js, but getting it to communicate with the API is not working at all when bearer tokens are required. I have used https://github.com/AzureAD/azure-activedirectory-library-for-js as template in addition to countless blogs.
The problem is that when I set up endpoints while initiating adal.js, adal.js seems to redirect all outgoing endpoint traffic to microsofts login service.
Observations:
Adal.js session storage contains two adal.access.token.key entries. One for the client ID of the SPA Azure AD application and one for the external api. Only the SPA token has a value.
If I do not inject $httpProvider into adal.js, then calls go out to the external API and I get a 401 in return.
If I manually add the SPA token to the http header ( authorization: bearer 'token value') I get a 401 in return.
My theory is that adal.js is unable to retrieve tokens for endpoints (probably because I configured something wrong in the SPA) and it stops traffic to the endpoint since it is unable to get a required token. The SPA token cannot be used against the API since it does not contain the required rights. Why is adal.js not getting tokens for endpoints and how can I fix it?
Additional information:
The client Azure AD application is configured to use delegated permissions against the API and oauth2AllowImplicitFlow = true in app manifest.
The API Azure AD application is configured for impersonation and oauth2AllowImplicitFlow = true (do not think that is required, but tried it). It is multi tenant.
The API is configured to allow all CORS origins and it works correctly when used by another web app using impersonation (hybrid MVC (Adal.net) + Angular).
Session storage:
key (for the SPA application): adal.access.token.keyxxxxx-b7ab-4d1c-8cc8-xxx value: eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik1u...
key (for API application): adal.access.token.keyxxxxx-bae6-4760-b434-xxx
value:
app.js (Angular and adal configuration file)
(function () {
'use strict';
var app = angular.module('app', [
// Angular modules
'ngRoute',
// Custom modules
// 3rd Party Modules
'AdalAngular'
]);
app.config(['$routeProvider', '$locationProvider',
function ($routeProvider, $locationProvider) {
$routeProvider
// route for the home page
.when('/home', {
templateUrl: 'App/Features/Test1/home.html',
controller: 'home'
})
// route for the about page
.when('/about', {
templateUrl: 'App/Features/Test2/about.html',
controller: 'about',
requireADLogin: true
})
.otherwise({
redirectTo: '/home'
})
//$locationProvider.html5Mode(true).hashPrefix('!');
}]);
app.config(['$httpProvider', 'adalAuthenticationServiceProvider',
function ($httpProvider, adalAuthenticationServiceProvider) {
// endpoint to resource mapping(optional)
var endpoints = {
"https://localhost/Api/": "xxx-bae6-4760-b434-xxx",
};
adalAuthenticationServiceProvider.init(
{
// Config to specify endpoints and similar for your app
clientId: "xxx-b7ab-4d1c-8cc8-xxx", // Required
//localLoginUrl: "/login", // optional
//redirectUri : "your site", optional
extraQueryParameter: 'domain_hint=mydomain.com',
endpoints: endpoints // If you need to send CORS api requests.
},
$httpProvider // pass http provider to inject request interceptor to attach tokens
);
}]);
})();
Angular code for calling endpoint:
$scope.getItems = function () {
$http.get("https://localhost/Api/Items")
.then(function (response) {
$scope.items = response.Items;
});
Ok, I've been bashing my head against the wall to figure this out. Trying to make my ADAL.js SPA app (sans angular) successfully make cross-domain XHR requests over to my precious CORS-enabled Web API.
This sample app, the one all the newbies like me are using, has this problem: it features an API and SPA all served from the same domain - and only requires a single AD Tenant app registration. This only confuses things when it comes time to pull things apart into separate pieces.
So, out of the box, the sample has this Startup.Auth.cs which works OK, as far as the sample goes...
public void ConfigureAuth(IAppBuilder app) {
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Audience = ConfigurationManager.AppSettings["ida:Audience"],
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
});
}
but, you need to modify the above code, drop the Audience assignment, and go for an array of audiences.. That's right: ValidAudiences .. So, for every SPA client that is talking to your WebAPI, you'll want to put the ClientID of your SPA registration in this array...
It should look like this...
public void ConfigureAuth(IAppBuilder app)
{
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
TokenValidationParameters = new TokenValidationParameters
{
ValidAudiences = new [] {
ConfigurationManager.AppSettings["ida:Audience"],//my swagger SPA needs this 1st one
"b2d89382-f4d9-42b6-978b-fabbc8890276",//SPA ClientID 1
"e5f9a1d8-0b4b-419c-b7d4-fc5df096d721" //SPA ClientID 2
},
RoleClaimType = "roles" //Req'd only if you're doing RBAC
//i.e. web api manifest has "appRoles"
}
});
}
EDIT
Ok, based on #JonathanRupp's feedback, I was able to reverse out the Web API solution I was using shown above, and was able to modify my client JavaScript as shown below to make everything work.
// Acquire Token for Backend
authContext.acquireToken("https://mycorp.net/WebApi.MyCorp.RsrcID_01", function (error, token) {
// Handle ADAL Error
if (error || !token) {
printErrorMessage('ADAL Error Occurred: ' + error);
return;
}
// Get TodoList Data
$.ajax({
type: "GET",
crossDomain: true,
headers: {
'Authorization': 'Bearer ' + token
},
url: "https://api.mycorp.net/odata/ToDoItems",
}).done(function (data) {
// For Each Todo Item Returned, do something
var output = data.value.reduce(function (rows, todoItem, index, todos) {
//omitted
}, '');
// Update the UI
//omitted
}).fail(function () {
//do something with error
}).always(function () {
//final UI cleanup
});
});
ADAL.js does get the access_token apart from id_token for calling Azure AD protected API running on different domain.
Initially, during login, it only takes id_token. This token has the access for accessing resource of the same domain.
But, on calling the API running in different domain, adal interceptor checks if the API URL is configured in as endpoint in adal.init().
It is only then that the access token is called for the requested resource. It also necessitates that the SPA is configured in the AAD to access API APP.
The key to achieve this is following:
1. Add endpoints in the adal.init()
var endpoints = {
// Map the location of a request to an API to a the identifier of the associated resource
//"Enter the root location of your API app here, e.g. https://contosotogo.azurewebsites.net/":
// "Enter the App ID URI of your API app here, e.g. https://contoso.onmicrosoft.com/TestAPI",
"https://api.powerbi.com": "https://analysis.windows.net/powerbi/api",
"https://localhost:44300/": "https://testpowerbirm.onmicrosoft.com/PowerBICustomServiceAPIApp"
};
adalProvider.init(
{
instance: 'https://login.microsoftonline.com/',
tenant: 'common',
clientId: '2313d50b-7ce9-4c0e-a142-ce751a295175',
extraQueryParameter: 'nux=1',
endpoints: endpoints,
requireADLogin: true,
//cacheLocation: 'localStorage', // enable this for IE, as sessionStorage does not work for localhost.
// Also, token acquisition for the To Go API will fail in IE when running on localhost, due to IE security restrictions.
},
$httpProvider
);
Give permission to the SPA application in Azure AD to access the API application:
You may refer this link for details : ADAL.js deep dive
You need to make your Web API aware of your Client application. It's not enough to add delegated permission to API from your Client.
To make the API client aware, go to Azure management portal, download API's manifest and add ClientID of your Client application to the list of "knownClientApplications".
To allow Implicit flow you need to set "oauth2AllowImplicitFlow" to true in the manifest as well.
Upload the manifest back to API application.
I'm not sure if our setup is exactly the same, but I think it it comparable.
I have a Angular SPA that uses and external Web API through Azure API Management (APIM). My code might not be best practice, but it works for me so far :)
The SPAs Azure AD app has a delegated permission to access the External APIs Azure AD app.
The SPA (is based upon the Adal TodoList SPA sample)
app.js
adalProvider.init(
{
instance: 'https://login.microsoftonline.com/',
tenant: 'mysecrettenant.onmicrosoft.com',
clientId: '********-****-****-****-**********',//ClientId of the Azure AD app for my SPA app
extraQueryParameter: 'nux=1',
cacheLocation: 'localStorage', // enable this for IE, as sessionStorage does not work for localhost.
},
$httpProvider
);
Snippet from the todoListSvc.js
getWhoAmIBackend: function () {
return $http.get('/api/Employee/GetWhoAmIBackend');
},
Snippets from the EmployeeController
public string GetWhoAmIBackend()
{
try
{
AuthenticationResult result = GetAuthenticated();
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
var request = new HttpRequestMessage()
{
RequestUri = new Uri(string.Format("{0}", "https://api.mydomain.com/secretapi/api/Employees/GetWhoAmI")),
Method = HttpMethod.Get, //This is the URL to my APIM endpoint, but you should be able to use a direct link to your external API
};
request.Headers.Add("Ocp-Apim-Trace", "true"); //Not needed if you don't use APIM
request.Headers.Add("Ocp-Apim-Subscription-Key", "******mysecret subscriptionkey****"); //Not needed if you don't use APIM
var response = client.SendAsync(request).Result;
if (response.IsSuccessStatusCode)
{
var res = response.Content.ReadAsStringAsync().Result;
return res;
}
return "No dice :(";
}
catch (Exception e)
{
if (e.InnerException != null)
throw e.InnerException;
throw e;
}
}
private static AuthenticationResult GetAuthenticated()
{
BootstrapContext bootstrapContext = ClaimsPrincipal.Current.Identities.First().BootstrapContext as BootstrapContext;
var token = bootstrapContext.Token;
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext authContext =
new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext("https://login.microsoftonline.com/mysecrettenant.onmicrosoft.com");
//The Client here is the SPA in Azure AD. The first param is the ClientId and the second is a key created in the Azure Portal for the AD App
ClientCredential credential = new ClientCredential("clientid****-****", "secretkey ********-****");
//Get username from Claims
string userName = ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn) != null ? ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn).Value : ClaimsPrincipal.Current.FindFirst(ClaimTypes.Email).Value;
//Creating UserAssertion used for the "On-Behalf-Of" flow
UserAssertion userAssertion = new UserAssertion(bootstrapContext.Token, "urn:ietf:params:oauth:grant-type:jwt-bearer", userName);
//Getting the token to talk to the external API
var result = authContext.AcquireToken("https://mysecrettenant.onmicrosoft.com/backendAPI", credential, userAssertion);
return result;
}
Now, in my backend external API, my Startup.Auth.cs looks like this:
The external API
Startup.Auth.cs
public void ConfigureAuth(IAppBuilder app)
{
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"],
SaveSigninToken = true
},
AuthenticationType = "OAuth2Bearer"
});
}
Please let me know if this helps or if I can be of further assistance.

thinktecture identity server 3 authentication works correctly in iis express, but keeps on throwing 401 unatuhorized when hosted in iis

Ok so i tried hosting the simplest oauth sample and the identity server both on iis, i have enable cors on the simplest oauth sample. So when i test the api using the javascript implicit client, on iis express it works flawlessly, it gets the token then when the token is sent the web api checks the token and authorizes the javascript client. the problem happens when i move the javascript imlicit client, the identity server, and the simple oath web api is hosted on iis, the javascript brings back the token correctly but when the token is sent to the web api it always return 401 unauthorized. So is there any configuration i have to add in order to run it on iis. i have made sure that anonymous authentication is the only enab;ed authentication mode. Any help or pointer is deeply appreciate.
I am trying to implement the samples given on iis. thanks for the help
I had the same issue. It was coming from my self signed certificate.
Try adding to your IdentityServerOptions
RequireSsl = false
and switch the WebApi Authority to use http.
Edit
Server Side Configuration
public void ConfigureIdentityServer(IAppBuilder app)
{
//Configure logging
LogProvider.SetCurrentLogProvider(new DiagnosticsTraceLogProvider());
//This is using a Factory Class that generates the client, user & scopes. Can be seen using the exmaples
var IdentityFactory = Factory.Configure("DefaultConnection");
app.Map("/identity", idsrvApp =>
{
idsrvApp.UseIdentityServer(new IdentityServerOptions
{
SiteName = "Security Proof of Concept",
SigningCertificate = LoadCertificate(),
Factory = IdentityFactory,
CorsPolicy = CorsPolicy.AllowAll,
RequireSsl = false
});
});
}
JavaScript
After receiving the token make sure it's inserted in the Authorization Header..
JQuery Example
$.ajax({
url: 'http://your.url',
type: GET,
beforeSend: function (xhr) {
xhr.withCredentials = true;
xhr.setRequestHeader("Authorization", " Bearer " + apiToken);
}
});
WebApi Resource
app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
//Location of identity server make full url & port
Authority = "http://localhost/identity",
RequiredScopes = new[] { "WebApiResource" }
//Determines if the Api Pings the Identity Server for validation or will decrypt token by it's self
//ValidationMode = ValidationMode.Local
});
Best way to determine what is happening is enable logging.