Sorry if this question is poorly worded, I am new to authentication.
I have an ASP.NET MVC project that serves my web frontend and this is authenticated using OWIN and identity cookie based authentication. This seems to work fine independently of my Web API.
I also have an ASP.NET Web API project that is also authenticated using OWIN and identity token based authentication e.g. make a request to the /Token endpoint and get a bearer token that can be used to make requests to the API endpoints. This works fine when called via postman using the bearer token generated via the /Token endpoint, but as I don't have the password when I want to call the API from the MVC application, I can't use the token endpoint to generate a token.
My problem is I would like to be able to make requests to the ASP.NET Web API from my authenticated ASP.NET MVC application, how would I go about generating a token that I can call the Web API? Given that I have a ClaimsIdentity that has been authenticated.
My Startup.Auth for my MVC project is:
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
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))
}
});
}
}
My Startup.Auth for my Web API project is:
public partial class Startup
{
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
public static string PublicClientId { get; private set; }
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
}
}
Thank you and please let me know if there's any further information that would be useful.
One option to consider, that I've implemented before, is to retrieve a token from the API upon successful login from the MVC application - using the same credentials that were passed in during login. Store the token how you please (i.e. in ASP.NET Session State) then use it as necessary in your application.
Your MVC application Login controller action could look something like this:
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: true);
switch (result)
{
case SignInStatus.Success:
BearerToken token;
using (var httpClient = new HttpClient())
{
var tokenRequest =
new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("grant_type", "password"),
new KeyValuePair<string, string>("username", model.Email),
new KeyValuePair<string, string>("password", model.Password)
};
HttpContent encodedRequest = new FormUrlEncodedContent(tokenRequest);
HttpResponseMessage response = httpClient.PostAsync("https://YourWebApiEndpoint/Token", encodedRequest).Result;
token = response.Content.ReadAsAsync<BearerToken>().Result;
// Store token in ASP.NET Session State for later use
Session["ApiAccessToken"] = token.AccessToken;
}
return RedirectToAction("SomeAction", "SomeController");
}
BearerToken is just a bespoke class representation of the full API token structure:
public class BearerToken
{
[JsonProperty("access_token")]
public string AccessToken { get; set; }
[JsonProperty("token_type")]
public string TokenType { get; set; }
[JsonProperty("expires_in")]
public string ExpiresIn { get; set; }
[JsonProperty("userName")]
public string UserName { get; set; }
[JsonProperty(".issued")]
public string Issued { get; set; }
[JsonProperty(".expires")]
public string Expires { get; set; }
}
An example call from the MVC application to retrieve some data might then look like this:
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Session["ApiAccessToken"].ToString());
var response = httpClient.GetAsync("https://YourWebApiEndpoint/SomeController/SomeGetAction").Result;
// Do something with response...
}
Related
I have created an Identity Server using .NET Core and IdentityServer4, I have set of APIs and all calls to these APIs must be authenticated but these APIs might be used by third-party applications so clients can be dynamic
Till now example I am finding is set Clients on startup statically like
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients());
}
public class Config
{
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource("resourceApi1", "API Application1")
new ApiResource("resourceApi2", "API Application2")
};
}
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "clientApp1",
// no interactive user, use the clientid/secret for authentication
AllowedGrantTypes = GrantTypes.ClientCredentials,
// secret for authentication
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedScopes = { "resourceApi1" }
}
};
}
}
Is there a way in IdentityServer implementation to register Client Apps and set dynamically
For Example, If I have APIs
1. resourceApi1
2. resourceApi2
Each third-party APIs should be able to register and we should be able to generate ClientID and Client secret for each with what resources they can access and Identity Server authenticates that ClientID and Client Secret?
There is an option to use a client store. By default identity server uses in memory store for finding clients:
services.AddIdentityServer()
.AddInMemoryClients(Clients)
You can change this and register your own client store
services.AddIdentityServer()
.AddClientStore<MyClientStore>()
and implement the service
class MyClientStore : IClientStore
{
public Task<Client> FindClientByIdAsync(string clientId)
{
}
}
This would solve dynamic lookup of clients. For registration of clients and their management, you would need to implement your own infrastructure.
First of all configure your IdentityServer using EntityFramework, Then you need to some Apis to add Client and ApiResources.
IdentityServer has its own implementation of Stores using EntityFramework.
You can add new Api for this purpose.
[Route("api/[controller]")]
[ApiController]
[AllowAnonymous]
public class DynamicController : ControllerBase
{
private readonly ConfigurationDbContext context;
public DynamicController(ConfigurationDbContext context)
{
this.context = context;
}
[HttpPost]
public async Task<ActionResult> AddApiResource(ApiResource apiResource)
{
context.ApiResources.Add(apiResource);
await context.SaveChangesAsync();
return Ok();
}
[HttpPost]
public async Task<ActionResult> AddClient(Client client)
{
context.Clients.Add(client);
await context.SaveChangesAsync();
return Ok();
}
}
I have this code that is supposed to set claims for a user. It works fine when I use identity and the default login. However, when I use jwt as authentication in another application, I don't have ApplicationUser as my ApplicationUser is stored in the other application that authenticates the user. How can I customize this code so that it works with jwt?
private readonly SignInManager<TIdentityUser> _signInManager;
public CustomClaimsCookieSignInHelper(SignInManager<TIdentityUser> signInManager)
{
_signInManager = signInManager;
}
public async Task SignInUserAsync(TIdentityUser user, bool isPersistent, IEnumerable<Claim> customClaims)
{
var claimsPrincipal = await _signInManager.CreateUserPrincipalAsync(user);
var identity = claimsPrincipal.Identity as ClaimsIdentity;
var claims = (from c in claimsPrincipal.Claims select c).ToList();
var savedClaims = claims;
if (customClaims != null)
{
identity.AddClaims(customClaims);
}
await _signInManager.Context.SignInAsync(IdentityConstants.ApplicationScheme,
claimsPrincipal,
new AuthenticationProperties { IsPersistent = isPersistent });
}
I guess my main intention is to set my users claims in the httpcontext and not in a cookie and I want to do that without using identity.
EDIT:
My application structure
AuthenticationApp (server)
Responsible for authenticating users
Generates and Decodes Jwt
Checks if the user has the appropriate roles and returns true/false via rest api
MainApp (client)
Makes an api call to AuthenticationApp
Does not use identity at all
Sends Jwt everytime I need to check the role of the user
I understand that I will be able to decode the jwt client side. However, I do not know where I can store the decoded jwt details so that I can use it in the view. My initial idea was to use Httpcontext like normal applications that user Identity. However, I am stuck with the code above.
For sharing the Identity information between Controller and View, you could sign the User information by HttpContext.SignInAsync.
Try steps below to achieve your requirement:
Controller Action
public async Task<IActionResult> Index()
{
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme, ClaimTypes.Name, ClaimTypes.Role);
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, "edward"));
identity.AddClaim(new Claim(ClaimTypes.Name, "edward zhou"));
//add your own claims from jwt token
var principal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal, new AuthenticationProperties { IsPersistent = true });
return View();
}
View
#foreach (var item in Context.User.Claims)
{
<p>#item.Value</p>
};
To make above code work, register Authentication in Startup.cs
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)
{
//your rest code
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//your rest code
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
I have a WebAPI 2.1 application and I am having a problem with User Registration. I placed a breakpoint on the first line of the Register method but it is not reached. Instead it fails in the area below:
public ApplicationUserManager UserManager
{
get
{
var a = Request; // this is null !!
return _userManager ?? Request.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
[AllowAnonymous]
[Route("Register")]
[ValidateModel]
public async Task<IHttpActionResult> Register(RegisterBindingModel model)
{
var user = new ApplicationUser() { // <<<<< Debug breakpoint here never reached
Email = model.Email,
FirstName = model.FirstName,
LastName = model.LastName,
OrganizationId = 1,
OrganizationIds = "1",
RoleId = (int)ERole.Student,
SubjectId = 1,
SubjectIds = "1",
UserName = model.UserName
};
System.ArgumentNullException was unhandled by user code
HResult=-2147467261
Message=Value cannot be null.
Parameter name: request
Source=System.Web.Http.Owin
ParamName=request
StackTrace:
at System.Net.Http.OwinHttpRequestMessageExtensions.GetOwinContext(HttpRequestMessage request)
at WebRole.Controllers.AccountController.get_UserManager() in c:\G\abr\WebRole\Controllers\Web API - Data\AccountController.cs:line 50
at WebRole.Controllers.AccountController.Dispose(Boolean disposing) in c:\G\ab\WebRole\Controllers\Web API - Data\AccountController.cs:line 376
at System.Web.Http.ApiController.Dispose()
at System.Web.Http.Cors.AttributeBasedPolicyProviderFactory.SelectAction(HttpRequestMessage request, IHttpRouteData routeData, HttpConfiguration config)
at System.Web.Http.Cors.AttributeBasedPolicyProviderFactory.GetCorsPolicyProvider(HttpRequestMessage request)
InnerException:
If anyone could give me any advice on where I could look to help solve this problem I would much appreciate it.
In particular can some explain to me the flow of how a request is handled in this configuration. I find it pretty confusing and I would like to know how the WebAPI and Owin fit together. Not knowing this is making it me difficult for me to understand the problem.
Thanks.
For reference here is my WebAPI start up class:
public partial class Startup
{
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
public static string PublicClientId { get; private set; }
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
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);
}
}
Update 1 - question correct after Darin's comments. The problem is not in the constructor.
Update 2 - Dispose Method:
protected override void Dispose(bool disposing)
{
if (disposing)
{
UserManager.Dispose();
}
base.Dispose(disposing);
}
Update 3 - Added the /Register method to show where I have a breakpoint (that's never reached)
There is no check for a null _userManager in your dispose method but the backing field can still be null. Also you access the UserManager property instead of using the backing field directly. So every time _userManager is null and the AccountController gets disposed the UserManager will try to create a new OwinContext. And that will fail.
Change your dispose method to:
protected override void Dispose(bool disposing)
{
if (disposing && _userManager != null)
{
_userManager.Dispose();
_userManager = null
}
base.Dispose(disposing);
}
The problem I have is in the Account constructor
The HTTP Context is not available in a controller constructor and this is by design. The earliest point in the execution where you can access it is after the Initialize method:
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
// This is the earliest stage where you can access the HTTP context (request, response, ...).
}
My application is an ASP.NET Identity 2 Web API application. In my Startup class I set AccessTokenExpireTimeSpan to 14 days:
public partial class Startup
{
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
public static string PublicClientId { get; private set; }
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = true
};
However I also noticed an example on the web where the following is set inside the
ApplicationUserManager class in the Create method:
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser>
(dataProtectionProvider.Create("ASP.NET Identity"))
{
TokenLifespan = TimeSpan.FromHours(1)
};
}
Can someone explain to me which I should be using:
Use the AccessTokenExpireTimeSpan in class OAuthAuthorizationServerOptions to set the expiry time for your access token.
The TokenLifespan property is used to set the life time for the Unique Code sent when you configure sending email confirmation and reset passwords. Maybe it should be named to something else to remove this ambiguity. If you are not sending resets passwords links then ignore this property.
Check this post too for complete example.
I have built a WebAPI for user login, the webAPI can generate Access Token, if the user provided correct UserName and password. My Question is how I can pass user role information to the MVC application also.
For example,
I have a MVC app controller below, how can I pass the role 'Admin, UserEditor' from the Web API? I know I can use another WebAPI call to check user role, but it is not a good idea to do it.
[Authorized("Admin,UserEditor")]
ActionResult EditUser(int? Id)
{
........
}
You can read role information from claims.
Step-1 Create Role-s
I created it seed, but your choice may be different.
public static class MyDbInitializer
{
public static void Seed(this ModelBuilder builder)
{
Guid adminRoleId = Guid.Parse("90a5d1bb-2cf0-4014-9f1a-2d9f644a2e22");
builder.Entity<IdentityRole<Guid>>().HasData(
new IdentityRole<Guid>
{
Id = adminRoleId,
Name = RoleIdentifier.admin,
NormalizedName = RoleIdentifier.admin.ToUpper(CultureInfo.GetCultureInfo("en-GB"))
});
}
}
Step-2 Claims
public static class RoleIdentifier
{
public const string admin = "admin";
public const string user = "user";
}
public static class JwtClaimIdentifier
{
public const string UserId = "user_id";
public const string UserName = "user_name";
public const string Role = "role";
}
Where you generate tokens, add the role name to the claims information.
...
... string role = await _userService.GetRole(userId);
... identity.FindFirst(JwtClaimIdentifier.Role)
Step-3 Add authorize att. to controllers.
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Roles = RoleIdentifier.admin)]
public class FooController
{
}
When the logged in user wants to access this action, the possession of this role will match and access claims.
You need to use 2 authentication mechanisms (Bearer Tokens, and Cookies) because your are securing Web API end points using tokens and MVC 5 controllers using Cookies. I recommend you to check VS 2013 Web template with MVC core dependency selected. It contains all the code needed at your case. Inside the GrantResourceOwnerCredentials method you will find something similar to the below:
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager,
OAuthDefaults.AuthenticationType);
ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager,
CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = CreateProperties(user.UserName);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}
Notice how there are oAuthIdentity for Web API, and cookiesIdentity for MVC application.