Cookie not being saved (Angular, Express) - javascript

Cookie received in chrome dev tools > network > cookies but not appearing in application > cookies
I have a login function that sets a cookie when a user logs in. It is appearing in the cookies column under the network tab but not in the application tab.
this is the function in express handling the cookie setting. This function is called when there is a post request made to the /login/ route.
const loginUser = async (req, res) => {
const dbRes = await authenticateUser(req.body);
if (dbRes.authenticated === true) {
res.setHeader(
"set-cookie",
`session=${dbRes.cookie};
path=/; samesite=lax`
);
// I've also tried using res.cookie()
res.status(200).json({ status: "authenticated" });
} else if (dbRes.authenticated === false) {
res.status(401).json({ status: "wrong username, email or password" });
} else {
res.status(dbRes.status).json({ error: dbRes.error });
}
};
Cookie setting works as expected when i do it like this and i go to the /cookie/ route in browser.
app.use("/cookie/", (req, res) => {
res.setHeader("set-cookie", "foo=bar");
res.send("set");
});
I have an angular frontend sending a post request to /login/ route but the login route is undefined in my angular app so the user will be redirected to / route.
import { NgModule } from '#angular/core';
import { RouterModule, Routes } from '#angular/router';
import { UserLoggedInGuard } from './guards/user-logged-in.guard';
const routes: Routes = [
{
path: '',
loadChildren: () =>
import('./features/login/login.module').then((m) => m.LoginModule),
},
{
path: 'feed',
loadChildren: () => import('./feed/feed.module').then((m) => m.FeedModule),
canActivate: [UserLoggedInGuard],
},
{
path: '**',
redirectTo: '',
},
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
However, the /cookie/ route set by express is not redirected and the cookie is being set correctly. I'm wondering if there are any conflicts between the angular and express router making setting cookies problematic.
I need help understanding why the cookie is not appearing in the application tab although it was received by the browser under the network tab.
--
As per Kavinda's answer, I tried adding useCredentials to my frontend code like so
loginUser(data: LoginForm) {
return this.httpClient.post(`${this.domainLink}/login`, data, {
withCredentials: true,
});
}
Unfortunately, this fix isnt working for me either even after setting credentials to true in the backend.
--
I've resorted to using ngx-cookie in Angular to set the cookie for now instead. However, I'd still appreciate it if someone can explain what I'm doing wrong here in the backend. Thanks!
I'll include my current workaround below in hopes it will help someone too
login.module.ts
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { FormsModule, ReactiveFormsModule } from '#angular/forms';
import { LoginRoutingModule } from './login-routing.module';
import { LoginComponent } from './login.component';
import { CookieModule } from 'ngx-cookie';
import { CookieService } from 'ngx-cookie';
#NgModule({
declarations: [LoginComponent],
imports: [
CommonModule,
LoginRoutingModule,
FormsModule,
ReactiveFormsModule,
CookieModule.withOptions(),
],
providers: [CookieService],
})
export class LoginModule {}
login.component.ts (res is the response from server where i returned a json value with the cookie value stored in the cookie key.
this.cookieService.put('session', res.cookie);

After testing it out it appears that chrome (86.0.4240.198) still allows cross site cookies on localhost which means that your problem is not caused by the new restrictions and using { useCredentials: true } should work fine.

Found my problem. I was running on the domain 'localhost' but my request link was going to '127.0.0.1' instead. Cookies cannot be set from a different domain.
Not setting the withCredentials flag to true originally didn't help my case either.

Related

How to use injected providers in a controller with a packaged dynamic module in nestjs

I couldn't seem to find any related question to my problem on here, so i decided to ask my first question, please be gentle :>
I want to provide an endpoint for frontend logging in several of our backends that use nestjs.
To solve this, i want to use a dynamic module that exposes said endpoint and package it as an npm package to be used in other applications.
I inject a logger into the controller via an injection token and the logging service via nestjs standard dependency injection.
The problem now is, after publishing the module, installing it in another application, the service i injected via nestjs standard DI is undefined.
I tried to follow the nestjs documentation as closly as possible when it comes to dynamic modules.
I also tried to retrieve the service via ModuleRef, but still the same problem. In this case, moduleRef is undefined.
Here is basically all of the involved code:
frontend-log.module.ts:
import { DynamicModule, Module } from "#nestjs/common";
import { FrontendLogService } from "./frontend-log.service";
import { Logger } from "./interfaces/logger";
import { FrontendLogController } from "./frontend-log.controller";
#Module({})
export class FrontendLogModule {
static forRoot(logger: Logger): DynamicModule {
return {
module: FrontendLogModule,
controllers: [FrontendLogController],
providers: [FrontendLogService, { provide: "LOGGER", useValue: logger }],
};
}
}
frontend-log.controller.ts:
import { Body, Controller, Inject, Post, Req } from "#nestjs/common";
import { FrontendLogMessage } from "../model/frontend-log-message";
import { Request } from "express";
import { RequestWithToken } from "../model/request-with-token";
import { Logger } from "./interfaces/logger";
import { ArrayMaxSize, ArrayMinSize, ValidateNested } from "class-validator";
import { Type } from "class-transformer";
import { FrontendLogService } from "./frontend-log.service";
class FrontendLogData {
#Type(() => FrontendLogMessage)
#ArrayMinSize(1)
#ArrayMaxSize(20)
#ValidateNested({ each: true })
messages: FrontendLogMessage[];
}
#Controller()
export class FrontendLogController {
constructor(
#Inject("LOGGER") private logger: Logger,
private logService: FrontendLogService
) {}
#Post("log")
public log(#Req() req: Request, #Body() data: FrontendLogData) {
this.logService.logFrontendMessages( // logService is undefined here
this.logger,
data.messages,
(req as RequestWithToken).kauth.grant.access_token.content
);
}
}
frontend-log.service.ts:
import { Injectable } from "#nestjs/common";
import { FrontendLogMessage } from "../model/frontend-log-message";
import { AccessTokenContent } from "../model/request-with-token";
import { Logger } from "./interfaces/logger";
#Injectable()
export class FrontendLogService {
public logFrontendMessages(
logger: Logger,
data: FrontendLogMessage[],
accessToken?: AccessTokenContent
) {
const user = accessToken
? `${accessToken.name} (${accessToken.sub})`
: "anonymous";
data.forEach((logEntry) => {
logger.log(logEntry.level, `[FRONTEND] [${user}] ${logEntry.message}`);
});
}
}
I then build the package and publish it.
After installing it in the consuming project I import the frontend-log.module into the AppModule as follows:
#Module({
imports: [
// ...
FrontendLogModule.forRoot(getLogger('debug')),
],
controllers: [
// ...
],
providers: [
// ...
],
})
export class AppModule implements NestModule {
// ...
}
When i then try to post to the /log endpoint via Postman, the following error occurs:
[Nest] 15280 - 26.10.2022, 09:03:50 ERROR [ExceptionsHandler] Cannot read properties of undefined (reading 'logFrontendMessages')
TypeError: Cannot read properties of undefined (reading 'logFrontendMessages')
at FrontendLogController.log
This tells me, the frontend-log.controller works properly, since the log endpoint is exposed as expected.
So my question: What am I doing wrong here, is there something i am not considering, when it comes to publishing nestjs Modules over npm? Or am I doing some rookie mistake here?

BrowserAuthError: interaction_in_progress - Unable to fix, regardles of solutions found

I'm implementing security for the applications at the company I'm working at right now. I'm using #azure/msal-angular#2.0.2, #azure/msal-browser#2.16.1. I followed the example found here
and got it working for the first application. I went on to implement it for the next application, which is basically the same one, just talks to a different api, but the complexity is the same. After possibly doing something wrong I keep getting the error:
core.js:6157 ERROR Error: Uncaught (in promise): BrowserAuthError: interaction_in_progress: Interaction is currently in progress. Please ensure that this interaction has been completed before calling an interactive API. For more visit: aka.ms/msaljs/browser-errors.
BrowserAuthError: interaction_in_progress: Interaction is currently in progress. Please ensure that this interaction has been completed before calling an interactive API. For more visit: aka.ms/msaljs/browser-errors.
I found several other posts that said to clear the caches, storages etc... But none of it works. All it does is prompt me to log in again, only to fill in the sessionStorage back with the same entries, amongst them the entry that says msal.442edcf7-9e0c-469b-93fb-daa1839724bd.interaction.status interaction_in_progress. As far as I know I've tried everthing I've found. Also the solution from AzureAD themselves doesn't work.
I'm very new to this so I might have missed something simple, if so, my apologies.
My code can be found below.
Angular version
11.0.2
app.module.ts
import {
MsalBroadcastService,
MsalGuard,
MsalInterceptor,
MsalModule, MsalRedirectComponent,
MsalService
} from "#azure/msal-angular";
import {BrowserCacheLocation, InteractionType, LogLevel, PublicClientApplication} from '#azure/msal-browser';
const isIE = window.navigator.userAgent.indexOf('MSIE ') > -1 || window.navigator.userAgent.indexOf('Trident/') > -1;
#NgModule({
declarations: [
AppComponent,
HeaderComponent,
RibonComponent
],
imports: [
BrowserModule,
AppRoutingModule,
...,
MsalModule.forRoot(
new PublicClientApplication({ // MSAL Configuration
auth: {
clientId: "442edcf7-...",
authority: "https://login.microsoftonline.com/81fa766e-...",
redirectUri: window.location.origin,
},
cache: {
cacheLocation : BrowserCacheLocation.LocalStorage,
storeAuthStateInCookie: false, // set to true for IE 11
},
system: {
loggerOptions: {
loggerCallback: (level: LogLevel, message: string, containsPii: boolean): void => {
if (containsPii) {
return;
}
switch (level) {
case LogLevel.Error:
console.error(message);
return;
case LogLevel.Info:
console.info(message);
return;
case LogLevel.Verbose:
console.debug(message);
return;
case LogLevel.Warning:
console.warn(message);
return;
}
},
piiLoggingEnabled: false
}
}
}), {
interactionType: InteractionType.Popup, // MSAL Guard Configuration
}, {
protectedResourceMap: new Map([
[ 'http://localhost:8400/', ['api://442edcf7-9e0c-469b-93fb-daa1839724bd/acces_as_user/Acces-user']],
[ 'https://quality-score-dev-pcq-dev.bravo-apps.volvocars.biz/', ['api://442edcf7-9e0c-469b-93fb-daa1839724bd/acces_as_user/Acces-user']]
]),
interactionType: InteractionType.Redirect // MSAL Interceptor Configuration
}
)
],
providers: [
EnvServiceProvider,
{
provide: MatPaginatorIntl,
useClass: MatPaginatorIntlTranslator
},
{
provide: HTTP_INTERCEPTORS,
useClass: MsalInterceptor,
multi: true
},
MsalService,
MsalGuard,
MsalBroadcastService,
],
bootstrap: [AppComponent]
})
export class AppModule { }
app.component.ts
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit, OnDestroy{
title = 'Quality score';
isIframe = false;
loginDisplay = false;
activeUser = '';
private readonly onDestroy$ = new Subject<void>()
constructor(
#Inject(MSAL_GUARD_CONFIG) private msalGuardConfig: MsalGuardConfiguration,
private languageService: LanguageService,
private authService: MsalService,
private msalBroadcastService: MsalBroadcastService,
private location: Location,
private userService: UserService
){}
ngOnInit(): void {
const currentPath = this.location.path();
// Dont perform nav if in iframe or popup, other than for front-channel logout
this.isIframe = BrowserUtils.isInIframe() && !window.opener && currentPath.indexOf("logout") < 0; // Remove this line to use Angular Universal
this.msalBroadcastService.inProgress$
.pipe(
filter((status: InteractionStatus) => status === InteractionStatus.None),
takeUntil(this.onDestroy$)
)
.subscribe(() => {
this.setLoginDisplay();
this.checkAndSetActiveAccount();
})
}
ngOnDestroy() {
this.onDestroy$.next();
}
setLoginDisplay() {
this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
}
checkAndSetActiveAccount(){
/**
* If no active account set but there are accounts signed in, sets first account to active account
* To use active account set here, subscribe to inProgress$ first in your component
* Note: Basic usage demonstrated. Your app may require more complicated account selection logic
*/
let activeAccount = this.authService.instance.getActiveAccount();
if (!activeAccount && this.authService.instance.getAllAccounts().length > 0) {
let accounts = this.authService.instance.getAllAccounts();
this.authService.instance.setActiveAccount(accounts[0]);
}
if(activeAccount) {
this.userService.activeUserName = activeAccount.name;
this.activeUser = activeAccount.name;
}
}
logout(): void{
this.authService.logoutRedirect();
}
}
I'm really lost and have no idea what I have done wrong. From my understanding there is a login process that was interupted, probably by me leaving the login screen, but now I have no idea how to "finish it up" or complete the process.
Update
I tried copying the LocalStorage values from the working application and I got it to work. Refreshing works and no errors appear, but when I logout and after it prompted me to login again and I do, then it's right back to the start again.
Solution update
I've had a breakthrough. If I change the login type to Popup and handle it this way, it's fixed. I can login and logout without any issues. However if I then change it back to Redirect, it's broken again. So for now I'll keep it on Popup. Simple solution, but I hadn't thought of it because I assumed the issue would be occur there as well.
I can't tell by the code provided, but I had a similar issue. On top of that I was getting the following error in the console: Error: The selector "app-redirect" did not match any elements
This lead me to the following post: https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/3114#issuecomment-788394239
I added <app-redirect></app-redirect> to the index.html page, below app-root as suggested and this seemed to sort both issues.
"Workaround" fix
Make your login type Popup. Dumb of me not to think about that

Read route params Angular

I'm working with Paypal API that after confirming the purchase it redirects to the url you want, I put the url that I wish would be "localhost:4200/shop/order".
however whenever paypal returns the url, they add the token and payerid at the end url
"localhost:4200/shop/order?token=8YS089366D9655&PayerID=ASDVD4BLMH",
however when it comes back to my angular application, i have an error saying that the page cannot be found.
I have tried several ways to configure the route, but all attempts have failed.
idk if Angular dont accept "?" and "&" in route.
const shopRoutingConfig:Routes = [
{
path:'',component:ShopAppComponent,
children:[
{path:'purchase',component:PurchaseComponent},
{
path:'order/:token/:payerid', //fail in url returned from paypal
component:OrderComponent
}
]
}
]
#NgModule({
imports:[
RouterModule.forChild(shopRoutingConfig)
],
exports:[RouterModule],
})
export class ShopRoutingModule{}
my order component:
export class OrderComponent implements OnInit
{
constructor(private route:ActivatedRoute) {
}
ngOnInit(): void {
debugger
const routeParams = this.route.snapshot.paramMap;
var tokenText = routeParams.get('token')
var userId = routeParams.get('PayerID')
}
}
the only way that worked , is if i edit url manually to
"localhost:4200/shop/order/8DC695025P9917207"
"localhost:4200/shop/order?token=8YS089366D9655&PayerID=ASDVD4BLMH" token and PayerId is query params, but you have described your route as order/:token/:payerId, which is Route params.
so it would have been worked if redirection URL would be
"localhost:4200/shop/order/8YS089366D9655/ASDVD4BLMH".
Since redirection URL is returning with queryParams, it would be better to set your route as
path:'order/', component: yourComponent
and in component.ts
constructor() {
this.route.queryParams.subscribe(params => {
this.tokenText = params.token:
this.userId = params.payerId
})
}

how to redirect to error component when service give error?

Could you please tell me how to redirect to component when service give error ?In my app I am requesting a service , I want if I got any error some service it will show error component
Here is my code
https://stackblitz.com/edit/angular-ctwnid?file=src%2Fapp%2Ftest.service.ts
In my current example I am using resolver to get data.
const routes: Routes = [
{
path: 'home',
component: HelloComponent,
resolve: { data: TestResolver }
},
{
path: 'error',
component: ErrorComponent
},
{
path: '',
redirectTo: '/home',
pathMatch: 'full'
}
];
I changed requested url so that I will get an error
correct url :https://jsonplaceholder.typicode.com/todos/1
wrong url :https://jsonplaceholder.typicode.com/todoss/1
I want that if I get an error it redirects to Errorcomponent.
#Injectable()
export class TestResolver implements Resolve<Observable<string>> {
constructor(private testService: TestService) {}
resolve(): Observable<any> {
return this.testService.getConfiguration();
}
}
any update ?
You have a couple of options. You could use a Guard to handle this, but because it's based on resolved data, it would probably be a good idea to make it an interceptor. Here's a post that should point you in the right direction re: interceptors
Using Router in services in Angular 4
You can handle this in your component.ts
Whenever the API call fails just use
this._service.function().subscribe(success => {
.....
},
error => {
this.router.navigate['error'];
});
where you call your service.

How to hold URL query params in Vue with Vue-Router

I am doing a project in Vue with Vue-Router . in my project ,i have a param named 'adtag' , which must be in the url query params , is there any simple way to hold this param ,no mater how router goes.
for example , I have three pages:
localhost/index
localhost/list
localhost/detail?id=11
page change using vue-router <router-link :to="{name:'Detail',query:{id:item.id}}"></router-link>
if I opened first page localhost/index?adtag=123 with adtag,page will changes with param 'adtag'
localhost/index?adtag=123
localhost/list?adtag=123
localhost/detail?adtag=123&id=11
With a default Vue 2.x installation, the router file is located src/router/index.js
I was able to then check if I needed to modify the request and add in any missing query params (modifying the to var apparently has no effect), and then call a "redirect" of next( .. new rout.. ).
Downside: Doubles the route calls, because essentially it redirects
Upside: It works, and the query preserving logic is in one place.
One caveat: On page load, the router fires and the "from" is a very empty route (even excluding the query params that were in the URL). Therefor I setup that if statement to verify the need to place the query param in place.
import Vue from 'vue'
import Router from 'vue-router'
// ... All your other components
Vue.use(Router)
const router = new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'Dashboard',
component: Dashboard
},
// ... All your other routes
]
})
router.beforeEach((to, from, next) => {
if (from.query.token && !to.query.token) {
if (to.path === from.path) {
// console.log('Identical routes detected')
return // This is a no-no via the documentation, but a bug in routing to identical routes strips query params, and this prevents that
}
next({path: to.path, query: {token: from.query.token}})
}
next()
})
export default router
As this is still an issue, I would even recommend using next(false) instead of returning.
if (from.query.foo && !to.query.foo) {
if (from.path === to.path) {
next(false);
} else {
next({
path: to.path,
query: { ...to.query, foo: from.query.foo },
});
}
} else {
next();
}
For reference, the same issue from the github repository: https://github.com/vuejs/vue-router/issues/1900#issuecomment-346531301.

Categories

Resources