I'm using an api REST in .net Core 2.2 implemented with JwtToken and an Angular 8 Application.
I'm trying to get the user Role to show it to the admin and give the possibility to edit this user role, but the role doesn't appear
[HttpGet]
[Authorize]
//GET: /api/UserProfile
public async Task<Object> GetUserProfile()
{
string userId = User.Claims.First(c => c.Type == "UserId").Value;
var user = await _userManager.FindByIdAsync(userId);
var role = await _userManager.GetRolesAsync(user);
return new
{
user.Email,
user.UserName,
user.Role,
};
}
As you know, user doesn't have a role property. Also each user has many roles. If you always have one role for each user, so select user's role like this:
[HttpGet]
[Authorize]
//GET: /api/UserProfile
public async Task<Object> GetUserProfile()
{
string userId = User.Claims.First(c => c.Type == "UserId").Value;
var user = await _userManager.FindByIdAsync(userId);
var role = await _userManager.GetRolesAsync(user);
return new
{
user.Email,
user.UserName,
role = role.FirstOrDefault() ,
};
}
Related
I have NodeJS web server using GraphQL using 2 connections. One has admin access, the other crud access.
Underlying Postgres DB has a Row Level Security policy, i.e.:
ALTER TABLE main.user ENABLE ROW LEVEL SECURITY;
CREATE POLICY user_isolation_policy ON main.user USING (id = current_setting('app.current_user_id')::UUID);
Before I login a user, I need to get their id from the db, then set the current_user_id variable in Postgres session while logging in.
However, when I try to fetch users back, I am expecting to get back only the logged in user, not everyone - this is how it behaves using pgAdmin. However, here I am getting the following error:
error: error: unrecognized configuration parameter "app.current_user_id"
Here is how I log a user in:
#Resolver()
export class LoginResolver {
#Mutation(() => LoginResponse)
public async login(
#Arg('email') email: string,
#Arg('password') password: string,
#Ctx() { res }: AppContext
): Promise<LoginResponse> {
try {
// get user from the admin repo so we can get their ID
const userRepository = (await adminConnection).getRepository(User)
const user = await userRepository.findOne({ where: { email } })
if (!user) throw new Error('user not found')
// using the api repo (not admin), set the variable
User.getRepository().query(`SET app.current_user_id TO "${user.id}"`)
const isValid = await bcrypt.compare(password, user.password)
if (!isValid) throw new Error('incorrect password')
if (!user.isConfirmed) throw new Error('email not confirmed')
sendRefreshToken(res, user.createRefreshToken())
return { token: user.createAccessToken() }
} catch (error) {
throw new Error(error)
}
}
}
Here is how I try to fetch back users:
#Resolver()
export class UsersResolver {
#Authorized(UserRole.admin, UserRole.super)
#Query(() => [User])
public users(): Promise<User[]> {
return User.find()
}
}
Please note that, if I remove the policy, GraphQL runs normally without errors.
The set variable is not persisting. How do I persist the variable while the user is logged in?
This approach works for me:
import { EntityManager, getConnection, getConnectionManager, getManager } from "typeorm";
import { EventSubscriber, EntitySubscriberInterface, InsertEvent, UpdateEvent, RemoveEvent } from "typeorm";
#EventSubscriber()
export class CurrentUserSubscriber implements EntitySubscriberInterface {
// get the userId from the current http request/headers/wherever you store it (currently I'm typeorm only, not as part of nest/express app)
async setUserId(mng: EntityManager, userId: string) {
await mng.query(`SELECT set_config('app.current_user_id', '${userId}', true);`)
}
async beforeInsert(event: InsertEvent<any>) {
await this.setUserId(event.manager, 'myUserId');
}
async beforeUpdate(event: UpdateEvent<any>) {
await this.setUserId(event.manager, 'myUserId');
}
async beforeRemove(event: RemoveEvent<any>) {
await this.setUserId(event.manager, 'myUserId');
}
}
Don't forget to configure the subscribers property in ormconfig.js, e.g. :
"subscribers": [
"src/subscribers/CurrentUserSubscriber.ts",
],
Have already raised this before and thought I have addressed it as per what suggested on THIS and THIS but seems not!
I am using ABP template (Angular and ASP .NET CORE Application) on Full .Net Framework. I simply want to use Windows Authentication to Authenticate user.
I added [Authorize] to the Authenticate in the TokenAuthController and have finally got the HttpContext.User.Identity.Name populated but only when I call the Authenticate from the Swagger (http://localhost:21021/swagger). But I am getting Unauthorized error when calling the method from Angular (login.service.ts):
POST http://localhost:21021/api/TokenAuth/Authenticate 401 (Unauthorized)
Here is the steps I have taken so far:
Changed launchSetting.json:
"iisSettings": {
"windowsAuthentication": true,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:21021/",
"sslPort": 0
}
},
Added ExternalAuthenticationSource:
public class WindowsAuthSource : DefaultExternalAuthenticationSource<Tenant, User>, ITransientDependency
{
public override string Name
{
get { return "Windows Authentication"; }
}
public override Task<bool> TryAuthenticateAsync(string userNameOrEmailAddress, string plainPassword, Tenant tenant)
{
return Task.FromResult(true);
}
}
Added it to CoreModule:
Configuration.Modules.Zero().UserManagement.ExternalAuthenticationSources.Add<WindowsAuthSource>();
4.Adjust AuthConfigurer:
services.AddAuthentication(opt => {
opt.DefaultScheme = IISDefaults.AuthenticationScheme;
opt.DefaultAuthenticateScheme = IISDefaults.AuthenticationScheme;
opt.DefaultChallengeScheme = IISDefaults.AuthenticationScheme;
});
Adjust StartUp.cs:
services.Configure<IISOptions>(iis =>
{
iis.AuthenticationDisplayName = "WINDOWS";
iis.AutomaticAuthentication = true;
});
Changed Authenticate method in the TokenAuthController:
public async Task<AuthenticateResultModel> Authenticate([FromBody]
AuthenticateModel model)
{
//var username = WindowsIdentity.GetCurrent().Name.Split('\\').Last();
var username = HttpContext.User.Identity.Name;
model.UserNameOrEmailAddress = username;
var loginResult = await GetLoginResultAsync(
model.UserNameOrEmailAddress,
model.Password,
null
);
var accessToken = CreateAccessToken(CreateJwtClaims(loginResult.Identity));
return new AuthenticateResultModel
{
AccessToken = accessToken,
EncryptedAccessToken = GetEncrpyedAccessToken(accessToken),
ExpireInSeconds = (int)_configuration.Expiration.TotalSeconds,
UserId = loginResult.User.Id
};
}
Sending dummy username and password from login.service.ts:
authenticate(finallyCallback?: () => void): void {
finallyCallback = finallyCallback || (() => { });
//Dummy data
this.authenticateModel.userNameOrEmailAddress = "DummyUsername";
this.authenticateModel.password = "DummyPassword";
this._tokenAuthService
.authenticate(this.authenticateModel)
.finally(finallyCallback)
.subscribe((result: AuthenticateResultModel) => {
this.processAuthenticateResult(result);
});
}
I'm trying to set up our IdentityServer solution to accept a custom Grant Validator. Our API project is accessed by to UIs, one that uses Password authentication (which is working) and now one that will use a 3rd party authentication.
In our API I've set up IdentityServer like so:
Startup.cs
public void Configuration(IAppBuilder app)
{
var factory = new IdentityServerServiceFactory()
.UseInMemoryClients(Clients.Get())
.UseInMemoryScopes(Scopes.Get());
var userService = new IdentityUserService();
factory.UserService = new Registration<IUserService>(resolver => userService);
factory.CustomGrantValidators.Add(
new Registration<ICustomGrantValidator, MyGrantValidator>());
var options = new IdentityServerOptions
{
SiteName = "My App Name",
SigningCertificate = Certificate.Get(),
Factory = factory
};
app.Map("/identity", identityServerApp =>
{
identityServerApp.UseIdentityServer(options);
});
}
MyGrantValidator.cs:
public class MyGrantValidator : ICustomGrantValidator
{
public async Task<CustomGrantValidationResult> ValidateAsync(ValidatedTokenRequest request)
{
// For now I just want a basic response. More logic will come later.
var authResult = new AuthenticateResult(
subject: "1234", // user.AccountId.ToString(),
name: "bob" //context.UserName
);
var grantResult = new CustomGrantValidationResult
{
IsError = authResult.IsError,
Error = authResult.ErrorMessage,
ErrorDescription = authResult.ErrorMessage,
Principal = authResult.User
};
return await Task.FromResult(grantResult);
}
public string GrantType => "myGrantType";
}
In my UI, I setup a client like this:
var owinContext = HttpContext.GetOwinContext();
var token = owinContext.Authentication.User.FindFirst(c => c.Type == "myToken")?.Value;
var tokenId = owinContext.Authentication.User.FindFirst(c => c.Type == ClaimTypes.Sid)?.Value;
var client = new TokenClient(
ConfigurationManager.AppSettings["IdentityServerBaseUrl"] + "/connect/token",
"MyUser",
ConfigurationManager.AppSettings["MyClientSecret"],
AuthenticationStyle.Custom
);
var tokenResponse = client.RequestCustomGrantAsync(
"myGrantType",
"read write",
new Dictionary<string, string>
{
{ "token", token },
{ "tokenId", tokenId }
}
).Result;
return Redirect(returnUrl);
When the Request is triggered, I get: unsupported_grant_type
What am I missing?
You're using a client called "MyUser" (weird name for a client, but ok). Is that client registered as one of the in-memory clients with grant type set to "custom"?
I'm trying to create in my WebApi controller an action to send an email with a passwors reset link. But when the email arrives, the link url is "http://Account/ResetPassword/........"
Please, note the site url is missing from de link. I need something like "http://www.domain.com/account/resetpassword/........."
This is my code. Thanks!
[System.Web.Http.AllowAnonymous]
[System.Web.Http.HttpGet]
[System.Web.Http.Route("ForgotPassword")]
public async Task<IHttpActionResult> ForgotPassword(string email)
{
if (string.IsNullOrWhiteSpace(email))
{
ModelState.AddModelError("", "Email is required");
return BadRequest(ModelState);
}
var user = await AppUserManager.FindByNameAsync(email);
if (user != null)
{
string code = await AppUserManager.GeneratePasswordResetTokenAsync(user.Id);
var urlHelper = new UrlHelper(HttpContext.Current.Request.RequestContext);
urlHelper.Action("ResetPassword", "Account", new RouteValueDictionary(new { userId = user.Id, code }), protocol: Request.RequestUri.Scheme);
await AppUserManager.SendEmailAsync(user.Id, "Redefinir Senha", "Redefina sua senha clicando aqui");
return Ok();
}
return NotFound();
}
I use MVC5 to get public profile user, but I just get ID and DisplayName of user. I have researched multi ways but have no result. This my setup snippet:
var options = new FacebookAuthenticationOptions
{
AppId = "xxx",
AppSecret = "xxx",
SignInAsAuthenticationType = "ExternalCookie",
Provider = new FacebookAuthenticationProvider
{
OnAuthenticated = async context =>
{
context.Identity.AddClaim(new System.Security.Claims.Claim("FacebookAccessToken", context.AccessToken));
}
}
};
options.Scope.Add("email");
options.Scope.Add("public_profile");
app.UseFacebookAuthentication(options);
And I call request result in action
public async Task<ActionResult> Index()
{
var userDisplayModel = new UserDisplayModel();
var authenticateResult = await AuthenticationManager.AuthenticateAsync("ExternalCookie");
if (authenticateResult != null)
{
userDisplayModel = new UserDisplayModel()
{
DisplayName = authenticateResult.Identity.Claims.FirstOrDefault(t => t.Type == ClaimTypes.Name).Value
};
}
return View(userDisplayModel);
}