Reputation: 14906
I am using the following service to get a JWT token to impersonate a user.
[Authenticate]
[RequiredRole(nameof(UserRoles.Admin))]
public class ImpersonateUserService : Service
{
private static ILog Log = LogManager.GetLogger(typeof(ImpersonateUserService));
public ImpersonateUserResponse Any(ImpersonateUserRequest request)
{
var user = Db.SingleById<UserAuthCustom>(request.UserId);
using (var service = base.ResolveService<AuthenticateService>()) //In Process
{
var resp = service.PostAsync(new Authenticate
{
provider = AuthenticateService.CredentialsProvider,
UserName = user.UserName ?? user.Email,
UseTokenCookie = true, // if using JWT
}).Result;
var token = ((AuthenticateResponse)resp).BearerToken;
return new ImpersonateUserResponse()
{
Success = true,
Token = token,
Email = user.Email,
DisplayName = user.DisplayName,
UserId = user.Id,
UserRole = (UserRoles)Enum.Parse(typeof(UserRoles), user.Roles[0]),
UserName = user.UserName ?? user.Email
};
}
}
}
public class ImpersonateUserRequest : IReturn<ImpersonateUserResponse>
{
public int UserId { get; set; }
}
public class ImpersonateUserResponse : ApiMessage
{
public string Token { get; set; }
public string Email { get; set; }
public string DisplayName { get; set; }
public string UserName { get; set; }
public int UserId { get; set; }
public UserRoles UserRole { get; set; }
}
The problem is that when the token is used it hydrates the admins details instead of the impersonated user. If I decode the impersonated token I can see:
{
"sub": 1,
"iat": 1623677160,
"exp": 1624886760,
"email": "[email protected]",
"given_name": "admin name",
"name": "admin name",
"roles": [
"Admin"
],
"jti": 9
}
This token will allow me to hit endpoints based on the impersonated users role (i.e. endpoints authenticated against "user" role instead of admin) but will bring back the admins details when requesting SessionAs<CustomUserSession>
.
What is best way to get a token with the right details inside it?
Upvotes: 2
Views: 267
Reputation: 14906
Going to answer my own question as way docs suggest impersonating user doesn't impersonate the session.
To get it working I had to fully create JWT in impersonation endpoint
var user = Db.SingleById<UserAuthCustom>(request.UserId);
var customSession = new CustomUserSession()
{
UserAuthId = user.Id.ToString(),
UserName = user.UserName,
DisplayName = user.DisplayName,
Email = user.Email,
IsAuthenticated = true,
Roles = user.Roles
};
var jwtProvider = new JwtAuthProvider(Settings)
{
AuthKeyBase64 = Settings.GetString("auth:key")
};
var header = JwtAuthProvider.CreateJwtHeader(jwtProvider.HashAlgorithm);
var body = JwtAuthProvider.CreateJwtPayload(
customSession,
issuer: jwtProvider.Issuer,
expireIn: jwtProvider.ExpireTokensIn,
roles: user.Roles
);
var jwtToken = JwtAuthProvider.CreateJwt(header, body, jwtProvider.GetHashAlgorithm());
return new ImpersonateUserResponse()
{
Success = true,
Token = jwtToken,
Email = user.Email,
DisplayName = user.DisplayName,
UserId = user.Id,
UserRole = (UserRoles)Enum.Parse(typeof(UserRoles), user.Roles[0]),
UserName = user.UserName ?? user.Email
};
Upvotes: 1