Im using asp.net core Identity. What I'm trying to do is when a user login again using the same credentials the previous login should be logged out. This is my code:
Login Controller Code
[HttpPost]
public async Task<IActionResult> Login(LoginDto loginDto){
if(!modelState.IsValid)
return View(logindto);
var user = await _userManager.FindByNameAsync(loginDto.username);
if (user.Session==false){
user.Session=true;
await _userManager.UpdateSecurityStampAsync(user);
user.LoginDate = DateTime.Now;
_context.SaveChanges();
}
Identity service extension
public static class IdentityServiceExtensions
{
public static IServiceCollection AddIdentityServices(this IServiceCollection services, IConfiguration config)
{
services.AddIdentityCore<AppUser>().AddRoles<Role>()
.AddEntityFrameworkStores<DataContext>().AddSignInManager<CustomSignInManager<AppUser>>();
services.Configure<SecurityStampValidatorOptions>(options=>{
options.ValidationInterval=TimeSpan.Zero;
});
//implement cookie
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(opt =>
{
opt.Cookie.Name = "cClient";
opt.Cookie.HttpOnly = true;
opt.LoginPath = "/Auth/Login/";
opt.LogoutPath = "/Auth/Logout/";
opt.ExpireTimeSpan = System.TimeSpan.FromMinutes(10);
opt.AccessDeniedPath = "/Error/Handle/401";
});
services.Configure<CookiePolicyOptions>(opt =>
{
opt.MinimumSameSitePolicy = SameSiteMode.Strict;
opt.ConsentCookie.HttpOnly = true;
});
services.AddMvc(opt =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
opt.Filters.Add(new AuthorizeFilter(policy));
});
services.AddAuthorization(opt =>
{
opt.AddPolicy("HoOnly", policy => policy.Requirements.Add(new HoOnly()));
});
return services;
}
}
}
Start up
public class Startup
{
private readonly IConfiguration _configuration;
public Startup(IConfiguration configuration)
{
_configuration = configuration;
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddApplicationServices(_configuration);
services.AddIdentityServices(_configuration);
services.AddAntiforgery(options =>
{
options.Cookie.SecurePolicy = Microsoft.AspNetCore.Http.CookieSecurePolicy.Always;
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
//security middleware
app.UseXContentTypeOptions();
app.UseReferrerPolicy(opt => opt.NoReferrer());
app.UseXXssProtection(opt => opt.EnabledWithBlockMode());
app.UseXfo(opt => opt.Deny());
app.UseCsp(opt => opt
.BlockAllMixedContent()
.StyleSources(s => s.Self().UnsafeInline())
.FontSources(s => s.Self())
.FormActions(s => s.Self())
.FrameAncestors(s => s.Self())
// .ImageSources(s => s.Self().CustomSources("https://www.w3.org/2000/svg"))
// .ScriptSources(s => s.UnsafeInline().Self().UnsafeEval().CustomSources("sha256-NkjQhhVxID3uKTZQylIN4GOkRMSDKaVyPr5YZsv+cTU="))
);
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.Use(async (context, next) =>
{
context.Response.Headers.Add("Strict-Transport-Security", "max-age=31536000");
await next.Invoke();
});
app.UseHsts();
}
app.UseStatusCodePagesWithReExecute("/Error/Handle/{0}");
app.UseHttpsRedirection();
app.UseAntiXssMiddleware();
//cookie
app.UseCookiePolicy();
app.UseStaticFiles();
app.UseRouting();
app.UseCors("CorsPolicy");
//auth
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}
But it does not sign out/logout the user. What am I doing wrong?
Start up
Identity service extension
Login Controller
Related
I need help trying to figure it out, i am using django as backend and Angular as frontend.
My django backend pass a token so it can be acessed in frontend by login.
login.ts
onLogin() {
this.service.loginUser(this.input).subscribe(
response => {
console.log(response)
this.jwttoken.setToken(response['token']);
this.jwttoken.getItem(response);
this.router.navigate(['dashboard']);
},
error => {
console.log('error', error);
}
i save it in a localstorage that can be acessed on my service
jwttoken service
jwtToken : string
decodedToken: {[key: string]: string}
public storage: Storage;
constructor() {
this.storage = window.localStorage;
}
setToken(token: string){
if (token) {
this.jwtToken = token;
}
}
getItem(key: string) {
if(this.storage){
localStorage.setItem('token',(key));
}
return null;
}
i need to have my user id that i can see on my web browser console.
{"token":"[object Object]","d34330659cba7cf64e8414f83aa6522f55b0f978":"d34330659cba7cf64e8414f83aa6522f55b0f978","[object Object]":"{"token":"d34330659cba7cf64e8414f83aa6522f55b0f978","user_id":1,"email":"admin#admin.com"}"}
this is where i need to access my user id, so i can send it to my html file
export class RegularComponent implements OnInit {
patient_code: any;
number_tumor: any;
tumor_size: any;
tumor_volume: any;
biopsy_date: any;
hp_lote_number: any;
closeResult: string;
userlists: any;
user_id: any;
constructor(private service: SharedService, private modalService: NgbModal, private jwtstorage: JWTTokenServiceService) { }
localstorage = JSON.stringify(this.jwtstorage.storage).replace(/\\/g, "");
ngOnInit() {
// this.getUserlist();
console.log(this.localstorage);
}
// getUserlist() {
// let observable = this.service.getUsersList();
// observable.subscribe((data) => { this.userlists = data; console.log(data); return data; }); }
open(content) {
this.modalService.open(content,{ariaLabelledBy: 'modal-basic-title', size: 'lg'}).result.then((result) => {
this.closeResult = `Closed with: ${result}`;
}, (reason) => {
this.closeResult = `Dismissed ${this.getDismissReason(reason)}`;
});
}
private getDismissReason(reason: any): string {
if (reason === ModalDismissReasons.ESC) {
return 'by pressing ESC';
} else if (reason === ModalDismissReasons.BACKDROP_CLICK) {
return 'by clicking on a backdrop';
} else {
return `with: ${reason}`;
}
}
}
I searched all around, and I can't figure it out. Could you explain to me how I can do this?
I just need the user_id of that string that cames from the localstorage. Thank you.
When creating a stock Blazor Server app (File/New) with Authentication for B2C you get a Startup.cs that looks like the following.
B2C itself is working, but I'm trying to simply change the Cookie name. By default it appears to be (.AspNetCore.AzureADB2CCookie)
How can I change it?
I've tried the following which doesn't appear to work:
1)
services.ConfigureApplicationCookie(options =>
{
options.Cookie.Name = UIConstants.WebSessionCookieName;
});
2)
.AddCookie(x =>
{
x.Cookie.Name = UIConstants.WebSessionCookieName;
});
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(AzureADB2CDefaults.AuthenticationScheme)
.AddAzureADB2C(options => Configuration.Bind("AzureAdB2C", options));
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddSingleton<WeatherForecastService>();
services.AddHttpContextAccessor();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
services.Configure<CookieAuthenticationOptions>(
AzureADB2CDefaults.CookieScheme, options =>
{
options.Cookie.Name = UIConstants.WebSessionCookieName;
});
I'm trying to set up unit tests for a sample Angular5 app using AngularFire2 (version5) google provider login, My auth service is fairly simple and it looks like this:
let authState = null;
let mockAngularFireAuth: any = {authState: Observable.of(authState)};
#Injectable()
export class AuthService {
loggedIn: boolean;
private user: Observable<firebase.User>;
constructor(
public afAuth: AngularFireAuth
) {
this.user = afAuth.authState;
this.user.subscribe(
(user) => {
if (user) {
this.loggedIn = true;
} else {
this.loggedIn = false;
}
});
}
// --------------------------------- Google Login -----------------------------------
loginWithGoogle() {
// Sign in/up with google provider
firebase.auth().setPersistence(firebase.auth.Auth.Persistence.SESSION)
.then(() => {
this.afAuth.auth.signInWithPopup(new firebase.auth.GoogleAuthProvider())
.catch((error) => {
if (error.code === 'auth/account-exists-with-different-credential') {
alert('This email address is already registered');
}
});
});
}
// ------------------------- Checks User Authentication -----------------------
isAuthenticated() {
// returns true if the user is logged in
return this.loggedIn;
}
// --------------------------------- User LogOut -----------------------------------
logOut() {
this.afAuth.auth.signOut()
.then(() => {
this.loggedIn = false;
});
}
}
I want to test my loginWithGoogle() method but I am not sure where to start. So far my auth service spec file looks like this:
describe('AuthService', () => {
let authService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
AngularFireDatabaseModule,
AngularFireModule.initializeApp(environment.firebase),
RouterTestingModule
],
providers: [
{provide: AngularFireAuth, useValue: mockAngularFireAuth},
AuthService,
]
});
inject([AuthService], (service: AuthService) => {
authService = service;
})();
});
it('should be defined', () => {
expect(authService).toBeDefined();
});
it('should return true if loggedIn is true', () => {
expect(authService.isAuthenticated()).toBeFalsy();
authService.loggedIn = true;
expect(authService.isAuthenticated()).toBeTruthy();
});
});
Any help would be appreciated.
Well, this is what I did. I mocked the AngularFireAuth and returned the promise with reject or resolve promise to be caught. I am new to jasmine and testing, so feel free to correct me if I am doing something wrong.
it('should return a rejected promise', () => {
authState = {
email: 'lanchanagupta#gmail.com',
password: 'password',
};
mockAngularFireAuth = {
auth: jasmine.createSpyObj('auth', {
'signInWithPopup': Promise.reject({
code: 'auth/account-exists-with-different-credential'
}),
}),
authState: Observable.of(authState)
};
mockAngularFireAuth.auth.signInWithPopup()
.catch((error: { code: string }) => {
expect(error.code).toBe('auth/account-exists-with-different-credential');
});
});
it('should return a resolved promise', () => {
authState = {
email: 'lanchanagupta#gmail.com',
password: 'password',
uid: 'nuDdbfbhTwgkF5C6HN5DWDflpA83'
};
mockAngularFireAuth = {
auth: jasmine.createSpyObj('auth', {
'signInWithPopup': Promise.resolve({
user: authState
}),
})
};
mockAngularFireAuth.auth.signInWithPopup()
.then(data => {
expect(data['user']).toBe(authState);
});
});
Why is User.Identity.IsAuthenticated == false when called via CORS, but true when called via same domain?
I have a working asp.net core 2 cookieauth app that is CORS enabled.
When I call;
api/Identity/establish-session
an AUTHCOOKIE gets dropped in both
CORS and local ajax calls.
Conversely when I call
api/Identity/sign-out
The AUTHCOOKIE gets removed. All good so far.
After a successful establish-session, when I call the following;
api/Identity/check-authentication
User.Identity.IsAuthenticated == false when called via CORS, but User.Identity.IsAuthenticated == true when called from the same domain.
I don't know if this is because of how I call it in javascript or if I have something configured wrong on the asp.net app. I thought I just had to have credentials: 'include' set in my fetch call?
[Produces("application/json")]
[Route("api/Identity")]
public class IdentityController : Controller
{
[HttpPost]
[AllowAnonymous]
[Route("establish-session")]
public async Task EstablishAuthenticatedSession(string username, string password)
{
var properties = new AuthenticationProperties
{
IsPersistent = true,
ExpiresUtc = DateTime.UtcNow.AddHours(1)
};
var claims = new[] {new Claim("name", username), new Claim(ClaimTypes.Role, "User")};
var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
await
HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(identity),
properties);
}
[HttpGet]
[AllowAnonymous]
[Route("sign-out")]
public async Task Logout()
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
}
[HttpGet]
[AllowAnonymous]
[Route("check-authentication")]
public async Task<bool> CheckAuthentication()
{
return User.Identity.IsAuthenticated;
}
}
Here is my javascript snippets;
establishAuthenticatedSession(){
let self = this;
var model = this.get();
console.log(model);
var url = "https://localhost:44310/api/Identity/establish-session?username=herb&password=1234";
fetch(url,
{
credentials: 'include',
headers: { 'Content-Type': 'text/plain' },
method: 'POST'
})
.then(function (res) {
console.log(res);
self.set({ establishSession:{ message:"Success" }});
}).catch(function(error) {
self.set({ establishSession:{ message:error.message }});
console.log('There has been a problem with your fetch operation: ' + error.message);
});
},
signOut(){
let self = this;
var model = this.get();
console.log(model);
var url = "https://localhost:44310/api/Identity/sign-out";
fetch(url,
{
credentials: 'include',
headers: { 'Content-Type': 'text/plain' },
method: 'GET'
})
.then(function (res) {
console.log(res);
self.set({ signoutResult:{ message:"Success" }});
}).catch(function(error) {
self.set({ signoutResult:{ message:error.message }});
console.log('There has been a problem with your fetch operation: ' + error.message);
});
},
checkAuthenticatedSession(){
let self = this;
var model = this.get();
console.log(model);
var url = "https://localhost:44310/api/Identity/check-authentication";
fetch(url,
{
credentials: 'include',
method: 'GET',
headers: { 'Content-Type': 'text/plain' }
})
.then(res => res.text())
.then(function (res) {
console.log(res);
self.set({ checkAuthenticatedSession:{ message:res }});
})
.catch(function(error) {
self.set({ checkAuthenticatedSession:{ message:error.message }});
console.log('There has been a problem with your fetch operation: ' + error.message);
});
}
This is my CORS setup;
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
So it turns out that the cookie needs to be set as SameSiteMode.None. The hint I got was that that ARRAfinity cookie from azure as set to non and it was being sent where mine was not.
In my app I had to set it as follows;
public class Startup
{
...
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
...
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
// sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(
CookieAuthenticationDefaults.AuthenticationScheme,
options =>
{
options.LoginPath = "/Account/LogIn"; ;
options.AccessDeniedPath = new PathString("/account/login");
options.Cookie.Name = "AUTHCOOKIE";
options.ExpireTimeSpan = new TimeSpan(365, 0, 0, 0);
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
options.Cookie.SameSite = SameSiteMode.None;
}
);
...
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
var cookiePolicyOptions = new CookiePolicyOptions
{
Secure = CookieSecurePolicy.SameAsRequest,
MinimumSameSitePolicy = SameSiteMode.None
};
app.UseCookiePolicy(cookiePolicyOptions);
...
}
}
I'm trying to implement unit test on login method but I'm getting "Cannot read property 'setRoot' of undefined" error.
Here is my Login Method:
import { inject, Aurelia, NewInstance, computedFrom } from 'aurelia-framework';
import { Router } from 'aurelia-router';
import { Server, User } from 'backend/server';
import { ValidationRules, ValidationController, validateTrigger } from 'aurelia-validation';
import { ValidationRenderer } from 'resources/validation-renderer';
#inject(Aurelia, Router, Server, NewInstance.of(ValidationController), NewInstance.of(User))
constructor(aurelia, router, server, validationController, user) {
this.router = router;
this.aurelia = aurelia;
this.validationController = validationController;
this.server = server;
this.user = user;
}
activate() {
this.validationController.validateTrigger = validateTrigger.blur;
this.validationController.addRenderer(new ValidationRenderer());
ValidationRules
.ensure(u => u.email)
.required()
.matches(/^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/)
.ensure('password')
.required()
.on(this.user);
}
logUserIn()
{
let promise = new Promise((resolve, reject) =>
{
this.loggingIn = true;
this.messageColor = 'green';
this.message = 'Authenticating...';
this.loginSuccess = false;
this.validationController.validate().then(errors => {
if (!errors.valid) {
this.loggingIn = false;
return ({error: true});
}
return this.server.authenticate(this.user.email, this.user.password);
}).then(result => {
if (!result.error) {
this.messageColor = 'green';
this.message = 'Login Successful';
this.loginSuccess = true;
this.aurelia.setRoot('shell/shell');
resolve(result);
} else {
this.message = '';
resolve();
}
}).catch(err => {
if(!this.loginSuccess)
{
this.messageColor = 'red';
this.message = err.message;
}
this.loggingIn = false;
resolve(err);
});
});
return promise;
}
My unit test code:
login.spec.js:
describe('Login Unit Test', () => {
var login = new Login();
login.validationController = new ValidationController();
login.server = new Server();
it("shouldn't allow login", (done) => {
console.log(login.messageColor);
login.logUserIn().then((result) => {
console.log(login.messageColor);
console.log(login.message);
expect(login.messageColor).toBe('red');
done();
});
});
it("Should log in", (done) => {
login.user = {email:'a#b.com', password:'abc'};
console.log(login.user.email);
console.log(login.user.password);
login.logUserIn().then((result) => {
console.log(login.messageColor);
console.log(login.message);
expect(login.messageColor).toBe('green');
done();
});
});
});
Here is the error that i'm getting
I would really appreciate any help.
Thanks.
Did you post exactly the Login code?
In this case before the constructor the #inject statement is missing.
Some like:
#inject(Aurelia, Router, Server, ValidationController, User)
constructor(aurelia, router, server, validationController, user)