I am trying to access the object values passed from source file to target file.
Here in the code below, I pass an object named auth from ServiceFile.js to ChildClassFile.js
But outside the ChildClassFile.js the properties inside auth object is undefined. Any help on how to access it would be appreciated. Thanks in advance.
Code:
// ServiceFile.js
import ChildClass from 'ChildClass';
export default class Service {
constructor(cookie, token) {
super();
this.auth = {
cookie: 'XXX',
token: 'XXX',
};
this.checkout = new ChildClass(this.auth);
}
async serviceMethod(data) {
return await this.checkout.method1(data);
};
}
// ChildClassFile.js
class ChildClass {
constructor(auth) {
this.auth = auth;
console.log("ChildClass -> constructor -> this.auth", this.auth); // values are present
}
async method1(data) {
return await axios.post({
data: data,
headers: {
token: this.auth.token,
cookie: this.auth.cookie
}, // undefined value for both token and cookie
});
};
}
There are some things wrong in your code. You are calling super in the Service class but it's not extending any other class.
I've noticed that you're not adding an URL to your POST request.
The following code should work:
class Service {
constructor() {
this.auth = {
cookie: 'XXX',
token: 'XXX'
};
this.checkout = new ChildClass(this.auth);
}
async serviceMethod(data) {
return await this.checkout.method1(data);
}
}
class ChildClass {
constructor(auth) {
this.auth = auth;
console.log('ChildClass -> constructor -> this.auth', this.auth); // values are present
}
async method1(data) {
console.log('ChildClass -> this.auth.token,', this.auth.token); // value is present
console.log('ChildClass -> this.auth.cookie', this.auth.cookie); // value is present
return await axios.post('where is the url?', {
data: data,
headers: {
token: this.auth.token,
cookie: this.auth.cookie
}
});
}
}
new Service()
.serviceMethod('dummy data')
.then(res => console.log('res', res))
.catch(err => console.log('err.message', err.message));
Related
I'm sending from frontend authorization token in headers and then I want to check validity of this token in some endpoints using middleware and context, but context is always empty.
I'm using type-graphql.
Frontend code (I check request in 'Network' tab and I can see my additional header):
private async mutate<T>(
mutation: DocumentNode,
data: unknown,
token?: string
) {
const response = await apolloClient.mutate<T>({
mutation: mutation,
context: {
headers: {
'auth-token': token || '',
},
},
variables: {
data: data,
},
});
return response.data;
}
Resolver code:
#Mutation(() => Token)
#UseMiddleware(authMiddleware)
async login(#Ctx() ctx: unknown, #Arg('data') data: LoginInput) {
console.log(ctx);
...
}
Middleware code:
export const authMiddleware: MiddlewareFn = ({ context }, next) => {
console.log(context);
try {
return next();
} catch (error) {
return next();
}
};
console.log is always equal to {}
I found the cause.
In declaration of ApollorServer the context was missing.
const server = new ApolloServer({
schema,
context: ({ req }) => {
const context = {
req,
};
return context;
},
cors: {
origin: '*',
credentials: true,
},
});
So I'm creating authentication logic in my Next.js app. I created /api/auth/login page where I handle request and if user's data is good, I'm creating a httpOnly cookie with JWT token and returning some data to frontend. That part works fine but I need some way to protect some pages so only the logged users can access them and I have problem with creating a HOC for that.
The best way I saw is to use getInitialProps but on Next.js site it says that I shouldn't use it anymore, so I thought about using getServerSideProps but that doesn't work either or I'm probably doing something wrong.
This is my HOC code:
(cookie are stored under userToken name)
import React from 'react';
const jwt = require('jsonwebtoken');
const RequireAuthentication = (WrappedComponent) => {
return WrappedComponent;
};
export async function getServerSideProps({req,res}) {
const token = req.cookies.userToken || null;
// no token so i take user to login page
if (!token) {
res.statusCode = 302;
res.setHeader('Location', '/admin/login')
return {props: {}}
} else {
// we have token so i return nothing without changing location
return;
}
}
export default RequireAuthentication;
If you have any other ideas how to handle auth in Next.js with cookies I would be grateful for help because I'm new to the server side rendering react/auth.
You should separate and extract your authentication logic from getServerSideProps into a re-usable higher-order function.
For instance, you could have the following function that would accept another function (your getServerSideProps), and would redirect to your login page if the userToken isn't set.
export function requireAuthentication(gssp) {
return async (context) => {
const { req, res } = context;
const token = req.cookies.userToken;
if (!token) {
// Redirect to login page
return {
redirect: {
destination: '/admin/login',
statusCode: 302
}
};
}
return await gssp(context); // Continue on to call `getServerSideProps` logic
}
}
You would then use it in your page by wrapping the getServerSideProps function.
// pages/index.js (or some other page)
export const getServerSideProps = requireAuthentication(context => {
// Your normal `getServerSideProps` code here
})
Based on Julio's answer, I made it work for iron-session:
import { GetServerSidePropsContext } from 'next'
import { withSessionSsr } from '#/utils/index'
export const withAuth = (gssp: any) => {
return async (context: GetServerSidePropsContext) => {
const { req } = context
const user = req.session.user
if (!user) {
return {
redirect: {
destination: '/',
statusCode: 302,
},
}
}
return await gssp(context)
}
}
export const withAuthSsr = (handler: any) => withSessionSsr(withAuth(handler))
And then I use it like:
export const getServerSideProps = withAuthSsr((context: GetServerSidePropsContext) => {
return {
props: {},
}
})
My withSessionSsr function looks like:
import { GetServerSidePropsContext, GetServerSidePropsResult, NextApiHandler } from 'next'
import { withIronSessionApiRoute, withIronSessionSsr } from 'iron-session/next'
import { IronSessionOptions } from 'iron-session'
const IRON_OPTIONS: IronSessionOptions = {
cookieName: process.env.IRON_COOKIE_NAME,
password: process.env.IRON_PASSWORD,
ttl: 60 * 2,
}
function withSessionRoute(handler: NextApiHandler) {
return withIronSessionApiRoute(handler, IRON_OPTIONS)
}
// Theses types are compatible with InferGetStaticPropsType https://nextjs.org/docs/basic-features/data-fetching#typescript-use-getstaticprops
function withSessionSsr<P extends { [key: string]: unknown } = { [key: string]: unknown }>(
handler: (
context: GetServerSidePropsContext
) => GetServerSidePropsResult<P> | Promise<GetServerSidePropsResult<P>>
) {
return withIronSessionSsr(handler, IRON_OPTIONS)
}
export { withSessionRoute, withSessionSsr }
Having a very very peculiar issue happening:
When i start my node application, i set my access tokens to an instance of a model like so:
index.js
const token = new Tokens();
token.setTokens(access_token, refresh_token);
console.log(token.getTokens()) // WORKS
I then call the getter functions in my instance in different files.
RunSchedular.js
const tokens = Tokens.getInstance();
console.log('sched',tokens.getTokens()) //WORKS
API.js
export const POSTRequest = () => {
const currentTokens = Tokens.getInstance();
const refreshToken = currentTokens.getRefreshToken(); // DOES NOT WORK
const body = {
method: 'POST',
headers:
{
"Content-Type": "application/x-www-form-urlencoded",
"Cache-Control": "no-cache"
},
body: qs.stringify({
client_secret: clientSecret,
client_id: clientId,
refresh_token: refreshToken,
grant_type: 'refresh_token',
redirect_uri: redirectUri
})
};
return body;
}
My model is like so:
let instance = null;
export default class Tokens {
constructor() {
if(!instance) {
instance = this;
}
this.accessToken = '';
this.refreshToken = '';
}
getAccessToken() {
return this.accessToken;
}
setAccessToken(value) {
this.accessToken = value;
}
getRefreshToken() {
return this.refreshToken;
}
setRefreshToken(value) {
this.refreshToken = value;
}
getTokens() {
return {
accessToken: this.accessToken,
refreshToken: this.refreshToken
}
}
setTokens(accessToken,refreshToken) {
this.accessToken = accessToken;
this.refreshToken = refreshToken;
}
static getInstance() {
console.log('instance', instance)
if(!instance) {
instance = new Tokens();
}
return instance;
}
};
Any ideas why this could be happening? The instance in the API.js does not return my access tokens (access token = '' as per the constructor) where as the schedular.js and index.js returns my access token fine?
Is my Model not correct?
How are you importing the Tokens module? If the path is different, 2 different modules will be imported.
E.G.:
import { Tokens } from 'src/singletons/Tokens';
Will be a different object than:
import { Tokens } from './../singletons/Tokens';
More information about singletons in javascript.
More information about module caching in NodeJS.
I'm a bit of a beginner with Angular so please bear with me.
I have a simple app which allows people to register, login and retrieve their own user data (which is the part I am stuck at).
Backend user.routes.js :
const auth = require('./middlewares/auth')
module.exports = (app) => {
const user = require('./user.controller.js');
app.post('/login', user.login);
app.post('/register', user.register);
app.get('/getuser', auth, user.getuser);
}
Backend user.controller.js:
exports.getuser = async (req, res, next) => {
let user
try {
user = await User.findById(req.payload._id)
} catch (err) {
next(new InternalServerError('Could not fetch user', err))
return
}
if (!user) {
next(new NotFoundError('User not found'))
return
}
res.json(
pick(user, [
'email',
'firstName',
'lastName',
'accountType'
])
)
}
Backend user.service.ts :
#Injectable()
export class UserService {
private _isLoggedIn: BehaviorSubject<boolean> = new BehaviorSubject(false);
public readonly isLoggedIn$ = this._isLoggedIn.asObservable();
constructor(private http: HttpClient) {
this._isLoggedIn.next(this.isLoggedIn());
}
login(
email: string,
password: string,
rememberMe = false
): Observable<boolean | any> {
return this.http
.post<LoginResponse>('http://localhost:3001/login', { email, password })
.map(res => {
setToken(res.token, rememberMe);
this._isLoggedIn.next(true);
return true;
})
.catch(this.handleError);
}
register(
email: string,
password: string,
lastName: string,
firstName: string
): Observable<boolean | any> {
return this.http
.post<LoginResponse>('http://localhost:3001/register', {
email,
password,
lastName,
firstName
})
.map(res => {
setToken(res.token);
return true;
})
.catch(this.handleError);
}
logout() {
removeToken();
}
isLoggedIn() {
return tokenNotExpired();
}
getProfile() {
return this.http.get<Profile>('http://localhost:3001/getuser');
}
And finally, my backend auth.js :
// Dependencies
import { JwtHelperService } from '#auth0/angular-jwt';
// Angular
import {
HttpEvent,
HttpHandler,
HttpInterceptor,
HttpRequest
} from '#angular/common/http';
import { Injectable } from '#angular/core';
// RXJS
import { Observable } from 'rxjs/Observable';
// Environment
import { DecodedToken } from './decoded-token';
// Services
const helper = new JwtHelperService();
// Constants
export const TOKEN_NAME = 'access_token';
// Exports
export function getToken(storage = null) {
if (storage) {
const token = storage.getItem(TOKEN_NAME);
if (token && !helper.isTokenExpired(token)) {
return token;
}
removeToken(storage);
return null;
}
return getToken(localStorage) || getToken(sessionStorage);
}
export function setToken(token: string, rememberMe = false) {
const storage = rememberMe ? localStorage : sessionStorage;
storage.setItem(TOKEN_NAME, token);
}
export function removeToken(storage = null) {
if (storage) {
storage.removeItem(TOKEN_NAME);
} else {
localStorage.removeItem(TOKEN_NAME);
sessionStorage.removeItem(TOKEN_NAME);
}
}
export function tokenNotExpired() {
return !helper.isTokenExpired(getToken());
}
export function decodeToken(): DecodedToken {
return helper.decodeToken(getToken());
}
#Injectable()
export class JwtHttpInterceptor implements HttpInterceptor {
constructor() {}
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
const token = getToken();
let clone: HttpRequest<any>;
if (token) {
clone = request.clone({
setHeaders: {
Accept: `application/json`,
'Content-Type': `application/json`,
Authorization: `Bearer ${token}`
}
});
} else {
clone = request.clone({
setHeaders: {
Accept: `application/json`,
'Content-Type': `application/json`
}
});
}
return next.handle(clone);
}
}
On my dashboard, I do a very simple request:
this.userService.getProfile().subscribe(data => (this.profile = data));
Now, my problem is the following:
Using Postman, if I do a POST request to /login, I get a token back. Everything fine so far. And if I use this token (in Postman) in my next GET request to /getuser, I also get the results I want (email, firstName, lastName, accountType of the user).
However, the problem is on the front-end. I login and arrive to the main page (no issues there), but once getProfile() is called, I get a GET http://localhost:3001/getuser 401 (Unauthorized) . I've been stuck on this for hours and not sure where the problem is from.
I appreciate any help I can get.
Thanks!
I found my issue. I had forgotten to add the Interceptor I had created to my providers in app.module.ts.
// Auth
{
provide: HTTP_INTERCEPTORS,
useClass: JwtHttpInterceptor,
multi: true
}
I'm trying to build a React app where users can save specific things under their ID.
I'm using nodeJS with React and auth0 for authentication.
I'm trying to access a property on the this.props.auth object and find if that property exists in my db so if there's a match something can be saved under the user's ID.
However I'm not able to access this.props.auth.id as shown in the code below but I can access this.props.auth
Any pointers?
.
.
.
Auth.js
import history from '../../history';
import auth0 from 'auth0-js';
import { AUTH0_CONFIG } from '../../auth0';
import API from "../../utils/API"
export default class Auth {
accessToken;
idToken;
expiresAt;
userProfile;
userImage;
name;
id;
auth0 = new auth0.WebAuth({
domain: AUTH0_CONFIG.domain,
clientID: AUTH0_CONFIG.clientId,
redirectUri: AUTH0_CONFIG.callbackUrl,
responseType: 'token id_token',
scope: 'openid profile'
})
constructor() {
this.login = this.login.bind(this);
this.logout = this.logout.bind(this);
this.handleAuthentication = this.handleAuthentication.bind(this);
this.isAuthenticated = this.isAuthenticated.bind(this);
this.getAccessToken = this.getAccessToken.bind(this);
this.getIdToken = this.getIdToken.bind(this);
this.renewSession = this.renewSession.bind(this);
this.userInfo = this.userInfo.bind(this)
}
login() {
this.auth0.authorize();
}
handleAuthentication() {
this.auth0.parseHash((err, authResult) => {
if (authResult && authResult.accessToken && authResult.idToken) {
this.setSession(authResult);
API.saveUser(authResult.idTokenPayload);
history.replace('/')
} else if (err) {
history.replace('/');
console.log(err);
alert(`Error: ${err.error}. Check the console for further details.`);
}
});
}
getAccessToken() {
return this.accessToken;
}
getIdToken() {
return this.idToken;
}
userInfo() {
return this.userProfile
}
setSession(authResult) {
// Set isLoggedIn flag in localStorage
localStorage.setItem('isLoggedIn', 'true');
console.log(authResult);
let expiresAt = (authResult.expiresIn * 1000) + new Date().getTime();
this.accessToken = authResult.accessToken
this.idToken = authResult.idToken;
this.expiresAt = expiresAt;
this.userImage = authResult.idTokenPayload.picture;
this.name = authResult.idTokenPayload.name.split(' ', 1);
this.id = authResult.idTokenPayload.nickname;
// navigate to the home route
history.replace('/');
}
renewSession() {
this.auth0.checkSession({}, (err, authResult) => {
if (authResult && authResult.accessToken && authResult.idToken) {
this.setSession(authResult)
console.log('authresult', authResult);
} else if (err) {
this.logout();
console.log(err);
alert(`Could not get a new token (${err.error}: ${err.error_description}).`);
}
});
}
logout() {
// Remove tokens and expiry time
this.accessToken = null;
this.idToken = null;
this.expiresAt = 0;
// Remove isLoggedIn flag from localStorage
localStorage.removeItem('isLoggedIn');
// navigate to the home route
history.replace('/');
}
isAuthenticated() {
// Check whether the current time is past the
// access token's expiry time
let expiresAt = this.expiresAt;
return new Date().getTime() < expiresAt;
}
}
Home.js
class Home extends Component {
constructor(props) {
super(props)
console.log(this.props); // can access this
console.log(this.props.auth.id); // this shows undefined
this.state = {
news: [],
summary:[],
summaryUrl: '',
userID: '',
user: '', //
pageLoading: true,
gistLoading: true
}
// console.log(this.state);
}
goTo(route) {
// console.log(history, route);
this.props.history.replace(`/${route}`)
}
login() {
this.props.auth.login();
}
logout() {
this.props.auth.logout();
}
// API call to display trending news
componentDidMount() {
API.getArticles()
.then(res => {
this.setState({
news: res.data,
pageLoading: false,
// user: this.props.auth.id
})
// console.log(this.state);
});
API.getSavedUsers()
.then((res) => {
console.log();
res.data.forEach((el) => {
console.log(this.props.auth.id); // shows undefined
if(el.name === this.props.auth.id){
this.setState({
userID: el.authID
})
} else {
console.log('notfound');
}
})
console.log(this.state);
})
const { renewSession } = this.props.auth;
if (localStorage.getItem('isLoggedIn') === 'true') {
renewSession();
}
}
I may be wrong but from the snapshot the data-type of auth property is Auth which is an object but if you look at it, match, location etc are all shown as {…} that symbolises its an object and hence we fetch the properties using dot. I would suggest parsing the auth first and then accessing the inner properties as follows:
const auth = JSON.parse(this.props.auth);
console.log(auth.id);
Could you try this for once.