0

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.

1

1 Answer 1

0

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

enter image description here

Not the answer you're looking for? Browse other questions tagged or ask your own question.