I have a console application SERVER that hosts WebApi controllers using OWIN self-hosting, and runs under a custom account named "ServiceTest1".
In the same machine I have another console application CLIENT that runs under the account "ServiceTest2", and I want to capture in SERVER that "ServiceTest2" invoked a controller action. However:
WindowsIdentity.GetCurrent() is always "ServiceTest1".
Thread.CurrentPrincipal is an unauthenticated GenericIdentity.
RequestContext.Principal is null.
User is null.
What do I need to make this WebApi OWIN self-hosted to grab the Windows identity of the caller?
Your question is a little unclear on exactly how you've implemented the Windows authentication.
Enable Windows authentication:
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpListener listener = (HttpListener)app.Properties["System.Net.HttpListener"];
listener.AuthenticationSchemes = AuthenticationSchemes.IntegratedWindowsAuthentication;
// ...
}
}
Get the user in an OWIN middleware:
public async Task Invoke(IDictionary<string, object> env)
{
OwinContext context = new OwinContext(env);
WindowsPrincipal user = context.Request.User as WindowsPrincipal;
//...
}
Get the user in a Web API Controller:
// In a web api controller function
WindowsPrincipal user = RequestContext.Principal as WindowsPrincipal;
Related
I'm working with a .net framework 4.7 app hosted in IIS. The api needs to be secured with JWT token. Identity is provided by another server and clients will send the JWT as bearer token in the header. I like to use OWIN pipeline for authorization. Currently the app uses Global.asax for startup and I like to keep it as is. I just want OWIN for authorization using JWT. I will use the [Authorize] attribute on the controllers needing jwt authorization. IIS doesn't do any authorization at the moment.
I have this in the Startup.cs for Owin.
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
TokenValidationParameters = new TokenValidationParameters()
{
ValidAudience = ConfigHelper.GetAudience(),
ValidIssuer = ConfigHelper.GetIssuer(),
ValidateLifetime = true,
ValidateIssuerSigningKey = true
}
});
}
}
How do I call the Startup.Configure() from Global.asax so Owin pipeline handles the authorization for incoming requests.
Thanks
You can have both global.asax and OWIN startup in the same project. ASP.NET will first call the global.asax and hand the control over to OWIN's startup. Make sure you have the Microsoft.Owin.Host.SystemWeb package installed in the project. And you have a class with the name Startup and a method Configuration(IAppBuilder app). There are other ways to let OWIN know where it should start.
You should also be aware of the fact that in .NET framework, there is a manual process to retrieve the signing keys from the authority that issued the JWT token. Otherwise, you will get the mismatched key error. Once you get the keys, you will assign them to ValidSigningKeys property in TokenValidationParameters. Search SO for examples.
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
TokenValidationParameters = new TokenValidationParameters()
{
ValidAudience = ConfigHelper.GetAudience(),
ValidIssuer = ConfigHelper.GetIssuer(),
ValidateLifetime = true,
ValidateIssuerSigningKey = true
}
});
}
I created a project in VS Community 2019 (latest update) with a template for WebApi .NET Core 2.1 and published it on Azure.
I only added a client secret in the app registration in the portal to use for the call using the authorization code flow.
I was trying to make a GET call using Postman with OAuth 2.0 authorization at the url below:
https://webapi3app.azurewebsites.net/api/values
But I get an unauthorized response with the error header below:
WWW-Authenticate:"Bearer error="invalid_token", error_description="The signature is invalid""
I tried decoding the client secret to BASE64 string but in the repsonse it says that it's an invalid client secret.
I tried changing authorization data to:
- Request URL.
- Request Headers.
I tried several grant types:
- Authorization code.
- Implicit.
- Password Credentials (after changing app to public client).
I tried several scopes:
- Default Microsoft scopes url (https://graph.microsoft.com/.default).
- user.read openid profile offline_access.
- https://aldolekalive.onmicrosoft.com/WebApi3/user_impersonation.
- profile openid email https://graph.microsoft.com/Directory.Read.All https://graph.microsoft.com/User.Read
I tried setting client authentication to:
- Send as basic auth header.
- Send client credentials in body.
I tried changing the Authorize attribute to authorize based on only AzureADBearer or only AzureADJwtBearer (because apparently by default they are both enabled with the current configuration) but no luck.
etc.
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.AzureAD.UI;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
.AddAzureADBearer(options => Configuration.Bind("AzureAd", options));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseMvc();
}
}
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "value1", "value2" };
}
}
I expect to get a response with the body:
"value1, value2"
Per my understanding, your webapi is protected by Azure AD and now you want to call the api. To call the api you need to provide an access token.
To do this, you need to register two applications in Azure AD. One is for client App, the other one is for webapi. You can refer to my answer here.
And here is the complete sample. If you don't have an client application now, you can just register an client app in Azure portal, then use this client app to get an access token for your webapi.
I tried several scopes:
If you are using v2.0 endpoint, the scope should be api://{server_client_id}/.default.
I need to implement role based authorization for asp .net web api
from native client application the access token will be sent in the header ,
so when i give [Authorize] attribute to my web api it is working fine,but when i give [Authorize(Role="Admin")] it is giving unauthorized error,
then whenever check for role in the claims it is always null.
My startup.cs is
public void Configuration(IAppBuilder app)
{
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Audience = ConfigurationManager.AppSettings["ida:Audience"],
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
});
}
and i have applied authorize attribute for a method
[Authorize]
[HttpGet]
public async Task<UserDetails> TestAuthorization()
{
string upn = ClaimsPrincipal.Current.FindFirst(ClaimTypes.Name).Value;
string tenantID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
var role = ClaimsPrincipal.Current.FindFirst(ClaimTypes.Role);
return null;
}
here the role value is always null
Please help me on this
I'm trying to implement IUserTokenProvider interface to register in Identity system and use further for authentication.
Why token authentication because i write Web Api server in Asp Net Core using Identity 3.
The obstacle is i don't know how to generate token. IUserTokenProvider has 2 method i need: 1. generates token 2. validates token.
what do i write inside them? what's algorithm?
public class UserTokenProvider : IUserTokenProvider<AppUser>
{
public Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<AppUser> manager, AppUser user)
{
throw new NotImplementedException();
}
public Task<string> GenerateAsync(string purpose, UserManager<AppUser> manager, AppUser user)
{
/* here logic to generate token */
string result = "generated token";
return Task.FromResult(result);
}
public Task<bool> ValidateAsync(string purpose, string token, UserManager<AppUser> manager, AppUser user)
{
/* validating token */
if (token == "generated token")
return Task.FromResult(true);
else return Task.FromResult(false);
}
}
and in ConfigureServices method of StartUp class i register my provider:
services.AddIdentity<AppUser, IdentityRole>(options => {
options.User.RequireUniqueEmail = true;
})
.AddEntityFrameworkStores<UserDbContext>()
.AddTokenProvider<UserTokenProvider>("AuthToken")
.AddDefaultTokenProviders();
As explained in this other SO post, IUserTokenProvider has absolutely nothing to do with token authentication.
I'd recommend reading these other questions for more information about how you can issue your own access tokens in ASP.NET Core:
Simple JWT authentication in ASP.NET Core 1.0 Web API.
Web API Authentication in ASP.NET 5.
Configure the authorization server endpoint.
I am looking to notify clients of my Web Api that other clients have authenticated using SignalR. I understand the basics of SignalR but I am confused as to how to modify the Hub such that it can work with an API request.
The client is a Single Page Application which makes use of jQuery to call the Web Api. The client first authenticates with the API before it can use any of the API endpoints.
Also should add that I am authenticating with OAuth in the same project as the Web Api.
Well, the easiest way here is to get a reference to the signalR hub inside the oauth authentication flux, and then launch a global signaling after the successfull completion of this method: Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context).
For example:
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
// auth and other stuffs removed for brevity
// ...
// ...
context.Validated(ticket);
// then here
var hub = GlobalHost.ConnectionManager.GetHubContext<IMessageHub>("MessageHub");
hub.Clients.All.ServerMessage(new {message = $"{context.UserName} is here!"});
}
with a message hub like this:
public interface IMessageHub
{
void ServerMessage(object eventObject);
}
[HubName("MessageHub"), Authorize]
public class MessageHub : Hub<IMessageHub>
{
public MessageHub()
{
}
}
Hope it helps :)