Blazor Server + Azure B2C Identity - Change Cookie Name - cookies

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;
});

Related

Can't validate security stamp in asp.net core

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

How to replace the authorize method in ember-simple-auth

I'm trying to refactor my Ember acceptance tests to not use the deprecated authorize method, as it is throwing a warning:
The `authorize` method should be overridden in your application adapter
I checked the docs, and numberous other sources, but they don't actually explain how to migrate my code. Here's what I've got at the moment:
// projectname/app/pods/login/controller.js (excerpt)
export default Controller.extend({
session: service(),
sessionToken: null,
onSuccess: function(res) {
res = res.response;
this.set('sessionToken', res.session);
if (res.state === "authenticated") {
document.cookie = "token="+res.session+";path=/;";
var authOptions = {
success: true,
data : {
session : res.session,
}
};
this.get('session').authenticate("authenticator:company", authOptions);
}
}
});
And this must be the part that I'm meant to get rid of:
// project/app/adapters/application.js (excerpt)
export default DS.RESTAdapter.extend(DataAdapterMixin, {
authorize(xhr) { // This is deprecated! I should remove it
let sessionToken = this.get('session.data.authenticated.session');
if (sessionToken && !isEmpty(sessionToken)) {
xhr.setRequestHeader('Authorization', "Token " + sessionToken);
}
},
});
And here is my test:
import { test, module } from 'qunit';
import { visit, currentURL, find, click, fillIn } from '#ember/test-helpers';
import { setupApplicationTest } from 'ember-qunit';
import { authenticateSession} from 'ember-simple-auth/test-support';
module('moduleName', function(hooks) {
setupApplicationTest(hooks);
test('moduleName', async function(assert) {
// await authenticateSession(this.application); // Never works
// await authenticateSession(); // Never works
await authenticateSession({
authenticator: "authenticator:company"
}); // Works slightly more?
await visit('/my/other/page');
await assert.equal(currentURL(), '/my/other/page');
});
});
REMOVING the authorize method and attempting either of the commented out methods yields:
Error: Assertion Failed: The `authorize` method should be overridden in your application adapter. It should accept a single argument, the request object.
If I use the authenticator block as an arg, then regardless of the presence of the authorize method, I simply get:
actual: >
/login
expected: >
/my/other/page
Which, I assume, is because it did not login.
Leaving the authorize method there, and trying the commented methods yields:
Error: Browser timeout exceeded: 10s
Per the docs you linked above: To replace authorizers in an application, simply get the session data from the session service and inject it where needed.
Since you need the session data in your Authorization header, a possible solution for your use case may look like this:
export default DS.RESTAdapter.extend(DataAdapterMixin, {
headers: computed('session.data.authenticated.session', function() {
const headers = {};
let sessionToken = this.get('session.data.authenticated.session');
if (sessionToken && !isEmpty(sessionToken)) {
headers['Authorization'] = "Token " + sessionToken;
}
return headers;
})
});
This should allow you to dynamically set the Authorization header, without doing so via the authorize method.
Ember Simple Auth, has an excellent community and quickly created a guide on how to upgrade to v3.
The latest version fixes this problem completely - If anyone is having this problem, upgrading to 2.1.1 should allow you to use the new format in your application.js:
headers: computed('session.data.authenticated.session', function() {
let headers = {};
let sessionToken = this.get('session.data.authenticated.session');
if (sessionToken && !isEmpty(sessionToken)) {
headers['Authorization'] = "Token " + sessionToken;
}
return headers;
}),
This problem was only present in 2.1.0.

Directory API users.list returns 403 with service account

I'm trying to use a Service Account to programmatically create users with the Admin Directory API.
The service account has been granted domain-wide delegation and has been authorized by an admin with the https://www.googleapis.com/auth/admin.directory.user scope.
As proof of concept, I tried to simply list all users on our domain with this code:
const google = require('googleapis');
const KEY = require('./mykey.json');
function connect() {
return new Promise((resolve, reject) => {
const jwtClient = new google.auth.JWT(
KEY.client_email,
null,
KEY.private_key,
['https://www.googleapis.com/auth/admin.directory.user'],
null
);
jwtClient.authorize((err) => {
if(err) {
reject(err);
} else {
resolve(jwtClient);
}
});
});
}
function listUsers(client) {
return new Promise((yep, nope) => {
google.admin('directory_v1').users.list({
auth: client,
domain: 'mydomain.com',
}, (err, response) => {
if (err) {
return nope(err);
}
return yep(response);
});
});
}
This is done in the context of a Firebase Cloud Function called like this:
function getAllUsers() {
return connect().then((client) => (listUsers(client)));
}
exports.getAllUsers = functions.https.onRequest((req, res) => {
getAllUsers().then((users) => {
res.status(200).send(users);
}).catch((err) => {
res.status(500).send(`Something went wrong :: ${err}`);
});
});
When I authenticate the request without impersonating a user, I get an error that says Not Authorized to access this resource/api. And when I authenticate the request while imitating an admin user (the second null in the connect() method above), I get an error that says Client is unauthorized to retrieve access tokens using this method.
I've followed all documentation to a T, and every related answer I've found seems to point to "domain-wide delegation" not being enabled, which it definitely is.
Grateful for any assistance...

WebAPI2 consume form angular2 error Not Found

I wont to consume Web Api2 async in Angular2 project, but i have some errors, when i try a request to the method.
My method is:
private baseUrl2 ='http://localhost:50348/api/Users/GetUsers'
getuser(): Observable<IUser[]>
{
console.log(this.baseUrl2);
return this.http.get(this.baseUrl2)
.map(res => res.json())
.do(data => console.log('getuser: ' + JSON.stringify(data)))
.catch(this.handleError);
}
My [HttpGet] method at WebApi is :
[HttpGet]
public IEnumerable<User> GetUsers()
{
return db.Users.ToList();
}
I have configure Microsoft.AspNetCore.Cors also and at Startup.cs i put AllowSpecificOrigin policy:
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("AllowSpecificOrigin",
builder => builder.WithOrigins("http://localhost:50348/"));
});
}
public void Configure(IApplicationBuilder app)
{
// Shows UseCors with named policy.
app.UseCors("AllowSpecificOrigin");
}
But when i run my angular project a call method form angular project i have an error as:
Object { _body: Object, status: 404, ok: false, statusText: "Not
Found", headers: Object, type: null, url:
"http://localhost:50348/api/Users/Ge…" }
When i try to call web api method from browser , not form angular project ,it works
[{"Country":"test","Id":1,"Name":"test","OtherInfo":"test","Surname":"test"}]
Whats wrong? Could you help me?
You can try
[HttpGet]
public IEnumerable<dynamic> Get()
{
return _context.Employee.ToList();
}

Ionic2 proxies not working with ionic run but working with ionic serve?

For my ionic.config.json I have:
{
"name": "TSICMobile",
"app_id": "6e4680fa",
"typescript": true,
"v2": true,
"proxies": [
{
"path": "/api",
"proxyUrl": "http://192.168.0.105:8081/api"
}
]
}
In my provider (user-data.ts, based on Ionic2 conference app) I have for example:
login(credentials) {
return new Promise((resolve, reject) => {
this.http.post(
'/api/Login',
JSON.stringify(credentials),
{ headers: this.contentHeader }
).subscribe(res => {
console.log('api/Login return');
this.data = res.json();
if (this.data.authenticated === true) {
this.storage.set('TSIC_USER_PROFILE', JSON.stringify(this.data.tsiC_USER_PROFILE));
this.storage.set('TSIC_USER_ROLES', JSON.stringify(this.data.listRoles));
this.storage.set('tsic_id_token', this.data.token);
this.events.publish('user:login');
resolve(true);
} else {
reject('not authenticated');
}
}, error => {
console.log('api/Login failed');
reject(error);
});
});
}
when running:
ionic serve --lab -c
the proxy works perfectly and posts to http://192.168.0.105:8081/api/Login
when running
ionic run android -c
the post url is file://api/Login, and obviously fails.
Need assistance in understanding why (seemingly), the proxy is not in effect when running on device, and what I may be doing wrong or not understanding.
You don't need a proxy when you are on your device because ionic can handle the cors there. You need the proxy on serve because the browser is trying to handle the CORS and its more strict with it.
What I suggest you do is check if window.cordova exists and if it does use the normal url and otherwise the proxy url.
Like this:
login(credentials) {
return new Promise((resolve, reject) => {
this.http.post(
window.cordova?:'http://192.168.0.105:8081/api/Login':'/api/Login':,
JSON.stringify(credentials),
{ headers: this.contentHeader }
).subscribe(res => {
console.log('api/Login return');
this.data = res.json();
if (this.data.authenticated === true) {
this.storage.set('TSIC_USER_PROFILE', JSON.stringify(this.data.tsiC_USER_PROFILE));
this.storage.set('TSIC_USER_ROLES', JSON.stringify(this.data.listRoles));
this.storage.set('tsic_id_token', this.data.token);
this.events.publish('user:login');
resolve(true);
} else {
reject('not authenticated');
}
}, error => {
console.log('api/Login failed');
reject(error);
});
});
}
Short answer is the proxy is really only useful for ionic serve. For ionic run you need to use cordova-plugin-whitelist
https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-whitelist/
What this means for you though is, you'll have to swap your URIs during build. So instead of just /api/myAwesomeService you'll actually have http://192.168.0.105:8081/api as your URI when running on a real device.
this official article exactly shows you how to deal with this situation.
http://blog.ionic.io/handling-cors-issues-in-ionic/
an easier way is defining a Constant just like this:
.constant('SERVER', {
// when not using proxy
//url: 'https://myextsite.com/api/public/index.php/v1'
// when using proxy
url: 'v1'
})
ref: https://forum.ionicframework.com/t/solved-ionicview-app-http-request-to-external-api/18696/3