Reputation: 340
Currently I am working on a task where I need to generate access token for a user based on user credentials(emailaddress and password) and some Id (e.g: subscriptionId ). As per my research there is no such provision to do so with the default IdentityServer behavior but I guess it can be achieved with add custom token generator.
Any help on this appreciated.
Upvotes: 0
Views: 50
Reputation: 12789
To generate the access token based on client id password and subscription you could use the custom token generator, below is the sample code you could try:
AccountController:
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
[ApiController]
[Route("api/[controller]")]
public class AccountController : ControllerBase
{
private readonly UserManager<IdentityUser> _userManager;
public AccountController(UserManager<IdentityUser> userManager)
{
_userManager = userManager;
}
[HttpPost("register")]
public async Task<IActionResult> Register([FromBody] RegisterModel model)
{
var user = new IdentityUser { UserName = model.Email, Email = model.Email };
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
return Ok();
}
return BadRequest(result.Errors);
}
}
public class RegisterModel
{
public string Email { get; set; }
public string Password { get; set; }
}
Program:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using IdentityServer4.Models;
using IdentityServer4.Validation;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseInMemoryDatabase("InMemoryDb"));
builder.Services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
builder.Services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryApiResources(GetApiResources())
.AddInMemoryClients(GetClients())
.AddInMemoryIdentityResources(GetIdentityResources())
.AddAspNetIdentity<IdentityUser>()
.AddResourceOwnerValidator<CustomResourceOwnerPasswordValidator>();
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseIdentityServer();
app.UseAuthorization();
app.MapControllers();
app.Run();
static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource("api1", "My API")
};
}
static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "client_id",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets = { new Secret("client_secret".Sha256()) },
AllowedScopes = { "api1", "openid", "profile" },
AllowOfflineAccess = true
}
};
}
static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile()
};
}
public class ApplicationDbContext : IdentityDbContext<IdentityUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
}
public class CustomResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
{
private readonly UserManager<IdentityUser> _userManager;
private readonly SignInManager<IdentityUser> _signInManager;
public CustomResourceOwnerPasswordValidator(UserManager<IdentityUser> userManager, SignInManager<IdentityUser> signInManager)
{
_userManager = userManager;
_signInManager = signInManager;
}
public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
{
var email = context.UserName;
var password = context.Password;
var subscriptionId = context.Request.Raw.Get("subscriptionId");
var user = await _userManager.FindByEmailAsync(email);
if (user != null && await _userManager.CheckPasswordAsync(user, password) && !string.IsNullOrEmpty(subscriptionId))
{
context.Result = new GrantValidationResult(
subject: user.Id,
authenticationMethod: "custom",
claims: GetUserClaims(user, subscriptionId));
return;
}
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Invalid credentials");
}
private IEnumerable<Claim> GetUserClaims(IdentityUser user, string subscriptionId)
{
return new List<Claim>
{
new Claim("email", user.Email),
new Claim("subscriptionId", subscriptionId)
};
}
}
To get the token with the subscription id you can use below request format:
POST /connect/token Content-Type: application/x-www-form-urlencoded
grant_type=password&username={username}&password={password}&client_id=client_id&client_secret=client_secret&subscriptionId=your_subscription_id
Upvotes: -1