Создать токен доступа с IdentityServer4 без пароля

Я создал ASP.NET Core WebApi, защищенный IdentityServer4, используя поток ROPC (используя этот пример: https://github.com/robisim74/AngularSPAWebAPI).

Как вручную сгенерировать access_token с сервера без пароля?

5 ответов

Решение
[HttpPost("loginas/{id}")]
[Authorize(Roles = "admin")]
public async Task<IActionResult> LoginAs(int id, [FromServices] ITokenService TS, 
    [FromServices] IUserClaimsPrincipalFactory<ApplicationUser> principalFactory,
    [FromServices] IdentityServerOptions options)
{
    var Request = new TokenCreationRequest();                        
    var User = await userManager.FindByIdAsync(id.ToString());
    var IdentityPricipal = await principalFactory.CreateAsync(User);
    var IdServerPrincipal = IdentityServerPrincipal.Create(User.Id.ToString(), User.UserName, IdentityPricipal.Claims.ToArray());

    Request.Subject = IdServerPrincipal;
    Request.IncludeAllIdentityClaims = true;
    Request.ValidatedRequest = new ValidatedRequest();
    Request.ValidatedRequest.Subject = Request.Subject;
    Request.ValidatedRequest.SetClient(Config.GetClients().First());
    Request.Resources = new Resources(Config.GetIdentityResources(), Config.GetApiResources());
    Request.ValidatedRequest.Options = options;
    Request.ValidatedRequest.ClientClaims = IdServerPrincipal.Claims.ToArray();

    var Token = await TS.CreateAccessTokenAsync(Request);
    Token.Issuer = "http://" + HttpContext.Request.Host.Value;

    var TokenValue = await TS.CreateSecurityTokenAsync(Token);
    return Ok(TokenValue);
}

Для недавно выпущенного IdentityServer 2.0.0 код нуждается в некоторых модификациях:

    [HttpPost("loginas/{id}")]
    [Authorize(Roles = "admin")]
    public async Task<IActionResult> LoginAs(int id, [FromServices] ITokenService TS,
        [FromServices] IUserClaimsPrincipalFactory<ApplicationUser> principalFactory,
        [FromServices] IdentityServerOptions options)
    {
        var Request = new TokenCreationRequest();
        var User = await userManager.FindByIdAsync(id.ToString());
        var IdentityPricipal = await principalFactory.CreateAsync(User);
        var IdentityUser = new IdentityServerUser(User.Id.ToString());
        IdentityUser.AdditionalClaims = IdentityPricipal.Claims.ToArray();
        IdentityUser.DisplayName = User.UserName;
        IdentityUser.AuthenticationTime = System.DateTime.UtcNow;
        IdentityUser.IdentityProvider = IdentityServerConstants.LocalIdentityProvider;
        Request.Subject = IdentityUser.CreatePrincipal();
        Request.IncludeAllIdentityClaims = true;
        Request.ValidatedRequest = new ValidatedRequest();
        Request.ValidatedRequest.Subject = Request.Subject;
        Request.ValidatedRequest.SetClient(Config.GetClients().First());
        Request.Resources = new Resources(Config.GetIdentityResources(), Config.GetApiResources());
        Request.ValidatedRequest.Options = options;
        Request.ValidatedRequest.ClientClaims = IdentityUser.AdditionalClaims;
        var Token = await TS.CreateAccessTokenAsync(Request);
        Token.Issuer = HttpContext.Request.Scheme + "://" + HttpContext.Request.Host.Value;
        var TokenValue = await TS.CreateSecurityTokenAsync(Token);
        return Ok(TokenValue);
    }

Использовать этот:
http://docs.identityserver.io/en/latest/topics/tools.html

Используйте этот инструмент, который поставляется с сервером идентификации:
Объявите это в конструкторе, чтобы получить путем внедрения зависимости.
IdentityServer4.IdentityServerTools _identityServerTools

    var isser = "http://" + httpRequest.Host.Value;  
    var token = await _identityServerTools.IssueJwtAsync(  
        30000,  
        эмитента,  
        новый System.Security.Claims.Claim[1]   
        {  
            новый System.Security.Claims.Claim("cpf", cpf)  
        });

Вот еще один способ добиться этого:

сначала создайте настраиваемый грант с именем loginBy

    public class LoginByGrant : ICustomGrantValidator
    {
        private readonly ApplicationUserManager _userManager;

        public string GrantType => "loginBy";

        public LoginByGrant(ApplicationUserManager userManager)
        {
            _userManager = userManager;
        }     

        public async Task<CustomGrantValidationResult> ValidateAsync(ValidatedTokenRequest request)
        {

            var userId = Guid.Parse(request.Raw.Get("user_id"));

            var user = await _userManager.FindByIdAsync(userId);

            if (user == null)
                return await Task.FromResult<CustomGrantValidationResult>(new CustomGrantValidationResult("user not exist"));

            var userClaims = await _userManager.GetClaimsAsync(user.Id);

            return
                await Task.FromResult<CustomGrantValidationResult>(new CustomGrantValidationResult(user.Id.ToString(), "custom", userClaims));

        }
    }

затем добавьте этот настраиваемый грант в класс запуска идентификатора

    factory.CustomGrantValidators.Add(
                        new Registration<ICustomGrantValidator>(resolver => new LoginByGrant(ApplicaionUserManager)));

и, наконец, в вашем API

      public async Task<IHttpActionResult> LoginBy(Guid userId)
       {
        var tokenClient = new TokenClient(Constants.TokenEndPoint, Constants.ClientId, Constants.Secret);

        var payload = new { user_id = userId.ToString() };

        var result = await tokenClient.RequestCustomGrantAsync("loginBy", "customScope", payload);

        if (result.IsError)
            return Ok(result.Json);

        return Ok(new { access_token = result.AccessToken, expires_in = result.ExpiresIn});
       }

Немного поздно отвечать.

в моем случае Generating Access Token Without Password был еще один identity server как организация sso, и наша реализация уже используется, поэтому нам нужно получить токен пользователя со второго (после входа пользователя в систему и перенаправления в наше приложение), извлечь sub, проверьте, существует ли он уже (если не вставьте в наш локальный IdentityServer), наконец, выберите пользователя и используйте новый грант, чтобы получить токен для пользователя. у вашего клиента должно быть это granttype как разрешенные типы грантов (здесь userexchange):

см. документацию по серверу идентификации или документацию duende для получения дополнительной информации.

          public class TokenExchangeGrantValidator : IExtensionGrantValidator {

        protected readonly UserManager<ToranjApplicationUser> _userManager;
        private readonly IEventService _events;

        public TokenExchangeGrantValidator(ITokenValidator validator, IHttpContextAccessor httpContextAccessor, UserManager<ToranjApplicationUser> userManager
            , IEventService events) {
            _userManager = userManager;
            _events = events;
        }


        public async Task ValidateAsync(ExtensionGrantValidationContext context) {
            var userName = context.Request.Raw.Get("uname");

            if (string.IsNullOrEmpty(userName)) {
                context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant);
                return;
            }

            var user = await _userManager.FindByNameAsync(userName);
            // or use this one, if you are sending userId
            //var user = await _userManager.FindByIdAsync(userId);
            if (null == user) {
                context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant);
                return;
            }

            await _events.RaiseAsync(new UserLoginSuccessEvent(user.UserName, user.Id.ToString(), user.UserName, false, context.Request.ClientId));
            var customResponse = new Dictionary<string, object>
                {
                {OidcConstants.TokenResponse.IssuedTokenType, OidcConstants.TokenTypeIdentifiers.AccessToken}
            };
            context.Result = new GrantValidationResult(
                subject: user.Id.ToString(),
                authenticationMethod: GrantType,
                customResponse: customResponse);
        }

        public string GrantType => "userexchange";
    }

в вашей startupс ConfigureServices после var builder = services.AddIdentityServer(...) добавьте свой недавно созданный класс.

          builder.AddExtensionGrantValidator<TokenExchangeGrantValidator>();

вызвать его для получения токена так же просто, как:

      POST /connect/token

grant_type=userexchange&
scope=yourapi&
uname=yourusername&
client_id=yourClientId
client_secret=secret

В дополнение к моему комментарию на ваш оригинальный вопрос. Реализовать функцию олицетворения в неявном / гибридном потоке. Если пользователь определен как "супер-администратор", то предоставьте ему дополнительный шаг после аутентификации, который позволит им войти / выбрать учетную запись, которую он хочет выдать. Как только это будет сделано, просто установите сеанс на сервере идентификации в качестве выбранного пользователя (и, возможно, сохраните дополнительные утверждения, обозначающие, что это олицетворенный сеанс и кто выполняет олицетворение). Любые токены будут выданы, как если бы вы были этим пользователем, и все без необходимости знать пароль.

Кроме того, если вы хотите создать токены самостоятельно, взгляните на ITokenCreationService, предоставляемый IdSrv4. Вы можете вставить это в свой собственный контроллер / службу / что угодно и использовать CreateTokenAsync(токен токена) для генерации подписанного JWT с любыми заявками, которые вам нравятся.

Другие вопросы по тегам