Cannot verify JWT token - express

I am using NestJS and its JWT package based on jsonwebtoken. The generated token is always being invalid, and I am getting a 500 - Internal Server Error.
What might be the problem?
My login function in the AuthService:
async login(email: string, password: string, isAdmin?: boolean, isVIP?: boolean){
let user = await this.usersService.findByEmail(email);
if(!user){
throw new NotFoundException('No user with this email could be found.');
}
const isEqual = await bcrypt.compare(password, user.password);
if(!isEqual){
throw new BadRequestException('Email and password do not match');
}
const secret = 'secretkey';
const payload = {email: user.email, userId: user._id.toString()}
const token = this.jwtService.sign(payload, {secret, expiresIn: '1h'});
return [email, isAdmin, isVIP, token];
}
My verification logic in the AuthGuard
`
import { BadRequestException, CanActivate, ExecutionContext, Inject } from "#nestjs/common";
import { JwtService } from "#nestjs/jwt/dist";
import { JwtConfigService } from "src/config/jwtconfig.service";
export class JwtAuthGuard implements CanActivate {
constructor(#Inject(JwtService) private jwtService: JwtService){}
canActivate(context: ExecutionContext) {
const request = context.switchToHttp().getRequest();
const authHeader = request.get('Authorization');
if (!authHeader) {
throw new BadRequestException('Not authorized');
}
const token = authHeader.split(' ')[1];
let decodedToken;
try {
decodedToken = this.jwtService.verify(token, {secret: 'secretkey'});
} catch (err) {
throw new Error('Cannot verify token.')
}
if(!decodedToken){
throw new BadRequestException('Not authenticated')
}
request.userId = decodedToken.userId;
console.log({decodedToken, token});
return request.userId;
};
}
My current JWT setup in the UsersModule imports (I have no AuthModule); I tried all the other configurations in the official docs, too.
JwtModule.register({
secret: 'secretkey',
publicKey: '...',
privateKey: '...',
secretOrKeyProvider: (
requestType: JwtSecretRequestType,
tokenOrPayload: string | Object | Buffer,
verifyOrSignOrOptions?: jwt.VerifyOptions | jwt.SignOptions
) => {
switch (requestType) {
case JwtSecretRequestType.SIGN:
return 'privateKey';
case JwtSecretRequestType.VERIFY:
return 'publicKey';
default:
return 'secretkey';
}
},
})
`
My jwtconfig.ts, which I don't think is being used:
`
import { JwtOptionsFactory, JwtModuleOptions } from '#nestjs/jwt'
export class JwtConfigService implements JwtOptionsFactory {
createJwtOptions(): JwtModuleOptions {
return {
secret: 'secretkey'
};
}
}
`

I solved the problem by switching my guard to a middleware.

Related

authorization header undefined, empty

Can you explain what I'm doing wrong, I don't understand why my header is empty. Login is working but when I trigger the me query it's not working, not authenticated appear and if I console log this it's undefined.
How to keep the user logged, and be able to acces the data ?
Here is my code, if you know what's wrong.
Thanks
import * as dotenv from 'dotenv';
dotenv.config();
import express from 'express';
import { ApolloServer } from 'apollo-server-express';
import { PrismaClient } from '#prisma/client';
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
const JWT_SECRET = 'abc123';
const typeDefs = `
type User {
id: ID
username: String
}
type Query {
me: User
}
type Mutation {
signup(username: String!, password: String!): User
login(username: String!, password: String!): User
}
`;
const resolvers = {
Query: {
async me(_, __, context) {
// Check if the request has a valid JWT
const auth = context.req.headers.authorization;
if (!auth) {
throw new Error('Not authenticated');
}
// Get the user ID from the JWT
const token = auth.split('Bearer ')[1];
const { userId } = jwt.verify(token, JWT_SECRET);
// Retrieve the user from the database
return context.prisma.user({ id: userId });
},
},
Mutation: {
async signup(_, { username, password }, context) {
// Hash the password
const hashedPassword = await bcrypt.hash(password, 10);
// Create a new user in the database
const user = await context.prisma.user.create({
data: {
username,
password: hashedPassword,
},
});
// Create a JWT for the new user
const token = jwt.sign({ userId: user.id }, JWT_SECRET);
return { token, ...user };
},
async login(_, { username, password }, context) {
// Retrieve the user from the database
const user = await context.prisma.user.findUnique({
where: {
username: username,
},
});
if (!user) {
throw new Error('Invalid login');
}
// Compare the provided password with the hashed password
const valid = await bcrypt.compare(password, user.password);
if (!valid) {
throw new Error('Invalid login');
}
// Create a JWT for the user
const token = jwt.sign({ userId: user.id }, JWT_SECRET);
return { ...user, token };
},
},
};
const main = async () => {
const app = express();
const prisma = new PrismaClient();
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => ({ req, prisma }),
});
await server.start();
server.applyMiddleware({ app });
app.listen(4000, () =>
console.log(`GraphQL server running on http://localhost:4000`)
);
};
main().catch((err) => {
console.error(err);
});

NextJS delete cookies and redirect from middleware

what I want to achieve is to delete 'accessToken' and 'refreshToken' cookies if both the accessToken and refreshToken are expired and then redirect to '/login' route.
This is my code now
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { verifyAccessToken, verifyRefreshToken } from './utils/checkAuth';
import jwt_decode from "jwt-decode";
import { refreshAccessToken } from './utils/refreshToken';
interface JwtPayload {
email: string
sub: number
iat: number
exp: number
}
export async function middleware(req: NextRequest) {
const accessToken = req.cookies.get('accessToken')?.value;
const refreshToken = req.cookies.get('refreshToken')?.value;
if(accessToken && refreshToken){
const jwtPayload: JwtPayload = jwt_decode(accessToken);
const validToken =
accessToken &&
(await verifyAccessToken(accessToken).catch((err) => {
console.log(err);
}));
if(req.nextUrl.pathname.startsWith('/login') && !validToken){
return
}
if((req.url.includes('/login') || req.url.includes('/register')) && validToken){
return NextResponse.redirect(new URL('/login', req.url));
}
if(!validToken){
try {
const validRefreshToken =
refreshToken &&
(await verifyRefreshToken(refreshToken).catch((err) => {
console.log(err);
}));
if(validRefreshToken){
const newAccessToken = await refreshAccessToken(refreshToken, jwtPayload?.sub);
console.log('GENERATED NEW ACCESS TOKEN', newAccessToken);
// here I want to set accesToken cookie to newAccessToken
} else {
console.log('Refresh token expired');
throw new Error('Refresh token expired')
}
} catch (error) {
console.log('cookies should be deleted');
return NextResponse.redirect(new URL('/login', req.url));
}
}
console.log('TOKEN VALID', accessToken);
} else {
if(req.nextUrl.pathname.startsWith('/login')){
return
} else {
return NextResponse.redirect(new URL('/login', req.url));
}
}
}
// See "Matching Paths" below to learn more
export const config = {
matcher: ['/', '/login', '/register', '/jobs/:path*', '/profile'],
}
I found out that by doing this
const response = NextResponse.next()
response.cookies.delete('accessToken')
response.cookies.delete('refreshToken')
this will work but then for this to actually delete the cookies I need to return "response" from the middleware but I also want to redirect the user to "/login" and this does not happen if I return "response" instead of returning the "NextResponse.redirect(new URL('/login', req.url))"
How can I remove the cookies or set the cookies and then also redirect ?

Why is firebase authentication not persisting on refresh in Ionic with Vue and Pinia?

I've been generally following along with this code here: https://github.com/aaronksaunders/ionic-v6-firebase-tabs-auth
The issue I'm having is my auth state is not persisting when I refresh the page when using ionic serve and loading the app in the web browser.
code for pinia store:
import { defineStore } from "pinia";
import { User } from "firebase/auth";
import {
Profile,
getProfile,
setProfile,
} from "#/firebase/helpers/firestore/profileManager";
import { onSnapshot, Unsubscribe, doc } from "#firebase/firestore";
import { db } from "#/firebase/connectEmulators";
import { getAuth } from "#firebase/auth";
import {
onAuthStateChanged,
signInWithEmailAndPassword,
signOut,
createUserWithEmailAndPassword,
updateProfile as updateAuthProfile,
} from "firebase/auth";
import errorHandler from "#/helpers/errorHandler";
/**#see {#link Profile} */
export enum UserType {
DNE,
uploader,
checker,
host,
}
interface State {
user: User | null;
profile: Profile | null;
error: null | any;
unsub: Unsubscribe | null;
}
export const useUserStore = defineStore("user", {
state: (): State => {
return {
user: null,
profile: null,
error: null,
unsub: null,
};
},
getters: {
isLoggedIn: (state) => state.user !== null,
//DEV: do we need this to be a getter?
userError: (state) => {
if(state.error){
switch (state.error.code) {
case "auth/user-not-found":
return "Email or Password incorrect!";
case "auth/wrong-password":
return "Email or Password incorrect!";
default:
return state.error;
}
}
return null;
},
/**
* #see Profile
*/
getType: (state): UserType => {
if (state.user === null) return UserType.DNE;
if (!state.profile) return UserType.DNE;
if (state.profile.locations.length > 0) return UserType.host;
if (state.profile.queues.length > 0) return UserType.checker;
return UserType.uploader;
},
},
actions: {
initializeAuthListener() {
return new Promise((resolve) => {
const auth = getAuth();
onAuthStateChanged(auth, (user) => {
console.log("AuthListener Initialized");
if (user) {
console.log("AuthListener: User exists!");
this.user = user;
getProfile(user.uid).then((profile) => {
if (profile) {
this.profile = profile;
this.initializeProfileListener();
} else {
this.profile = null;
if (this.unsub) this.unsub();
}
});
} else {
console.log("AuthListener: User does not exist!");
this.user = null;
}
resolve(true);
});
});
},
/**
*
* #param email email for login
* #param password password for login
*/
async signInEmailPassword(email: string, password: string) {
try {
const auth = getAuth();
const userCredential = await signInWithEmailAndPassword(
auth,
email,
password
);
this.user = userCredential.user ? userCredential.user : null;
this.error = null;
return true;
} catch (error: any) {
console.log(typeof error.code);
console.log(error.code);
this.user = null;
this.error = error;
return false;
}
},
async logoutUser() {
try {
const auth = getAuth();
await signOut(auth);
this.user = null;
this.profile = null;
this.error = null;
if (this.unsub) this.unsub();
return true;
} catch (error: any) {
this.error = error;
return false;
}
},
async createEmailPasswordAccount(
email: string,
password: string,
userName: string,
refSource: string
) {
try {
const auth = getAuth();
const userCredential = await createUserWithEmailAndPassword(
auth,
email,
password
);
//Add username to fireauth profile
//DEV: test for xss vulnerabilities
await updateAuthProfile(userCredential.user, { displayName: userName });
//create user profile data in firestore
let profile: Profile | undefined = new Profile(
userCredential.user.uid,
refSource
);
await setProfile(profile);
profile = await getProfile(userCredential.user.uid);
//set local store
this.user = userCredential.user ? userCredential.user : null;
this.profile = profile ? profile : null;
this.error = null;
//TODO: send email verification
return true;
} catch (error: any) {
this.user = null;
this.error = error;
return false;
}
},
initializeProfileListener() {
try {
if (!this.profile) errorHandler(Error("Profile not set in state!"));
else {
const uid = this.profile.uid;
const unsub: Unsubscribe = onSnapshot(
doc(db, "profiles", uid),
(snapshot) => {
const fbData = snapshot.data();
if (!fbData)
errorHandler(Error("Profile Listener snapshot.data() Null!"));
else {
const profile = new Profile(
snapshot.id,
fbData.data.referralSource
);
profile.data = fbData.data;
profile.settings = fbData.settings;
profile.locations = fbData.locations;
profile.queues = fbData.queues;
profile.checkers = fbData.checkers;
profile.uploadHistory = fbData.uploadHistory;
profile.hostLevel = fbData.hostLevel;
this.profile = profile;
}
},
(error) => {
errorHandler(error);
}
);
this.unsub = unsub;
}
} catch (error) {
errorHandler(error as Error);
}
},
},
});
main.ts where I initialize auth listener:
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import { IonicVue } from "#ionic/vue";
/* Core CSS required for Ionic components to work properly */
import "#ionic/vue/css/core.css";
/* Basic CSS for apps built with Ionic */
import "#ionic/vue/css/normalize.css";
import "#ionic/vue/css/structure.css";
import "#ionic/vue/css/typography.css";
/* Optional CSS utils that can be commented out */
import "#ionic/vue/css/padding.css";
import "#ionic/vue/css/float-elements.css";
import "#ionic/vue/css/text-alignment.css";
import "#ionic/vue/css/text-transformation.css";
import "#ionic/vue/css/flex-utils.css";
import "#ionic/vue/css/display.css";
/* Theme variables */
import "./theme/variables.css";
/* PWA elements for using Capacitor plugins */
import { defineCustomElements } from "#ionic/pwa-elements/loader";
/* Pinia used for state management */
import { createPinia } from "pinia";
import { useUserStore } from "./store/userStore";
const pinia = createPinia();
const app = createApp(App)
.use(IonicVue, {
// TODO: remove for production
mode: process.env.VUE_APP_IONIC_MODE,
})
.use(pinia);
defineCustomElements(window);
//get the store
const store = useUserStore();
store.initializeAuthListener().then(() => {
app.use(router);
});
router.isReady().then(() => {
app.mount("#app");
});
I've tried refactoring main.ts to mount the app inside the callback for initialize auth listener and I've tried making my code exactly like the code in the main.ts of the above link. Neither solved the issue.
I also looked at the question here: https://stackoverflow.com/a/67774186/9230780
Most of the points in the answer shouldn't be related because I'm currently using the firebase emulators to test the app.
Even still, I've verified my api key is correct.
I can see that cookies are created in the browser when I launch the app, so I don't think its an issue with them being wiped.
Ideally I'd like to avoid implementing #capacitor/storage here because it shouldn't be necessary.
I do plan to implement this library to handle authentication for ios and android: https://github.com/baumblatt/capacitor-firebase-auth
but that shouldn't be pertinent to the web version of the app.
Edit:
Realized I was missing a piece of code pertinent to the question. Not sure how I didn't copy it over. Code added is the initialize Profile listener function.
I ended up doing a refactor of the pinia store and that solved the issue. I believe the issue may have been caused by how the auth listener called initializeProfileListener. I didn't have code in the auth listener to check if the profile listener was already initialized, so everytime the authstate changed or it would initialize a new profile listener without unsubbing the old one. I'm not absolutely certain that is what was causing the issue though.
Below is the new code that functions properly.
pinia store:
import { defineStore } from "pinia";
import { User } from "firebase/auth";
import {
Profile,
getProfile,
profileListener,
} from "#/firebase/helpers/firestore/profileManager";
import {
fbCreateAccount,
fbSignIn,
fbAuthStateListener,
fbSignOut,
} from "#/firebase/helpers/firestore/authHelper";
import {Unsubscribe} from "#firebase/firestore";
import errorHandler from "#/helpers/errorHandler";
/**#see {#link Profile} */
export enum UserType {
DNE,
uploader,
checker,
host,
}
interface State {
user: User | null;
profile: Profile | null;
error: null | any;
unsub: Unsubscribe | null;
}
export const useUserStore = defineStore("user", {
state: (): State => {
return {
user: null,
profile: null,
error: null,
unsub: null,
};
},
getters: {
isLoggedIn: (state) => state.user !== null,
//DEV: do we need this to be a getter?
userError: (state) => {
if (state.error) {
switch (state.error.code) {
case "auth/user-not-found":
return "Email or Password incorrect!";
case "auth/wrong-password":
return "Email or Password incorrect!";
default:
return state.error;
}
}
return null;
},
/**
* #see Profile
*/
getType: (state): UserType => {
if (state.user === null) return UserType.DNE;
if (!state.profile) return UserType.DNE;
if (state.profile.locations.length > 0) return UserType.host;
if (state.profile.queues.length > 0) return UserType.checker;
return UserType.uploader;
},
},
actions: {
initializeAuthListener() {
return new Promise((resolve) => {
fbAuthStateListener(async (user: any) => {
if (user) {
this.user = user;
const profile = await getProfile(user.uid);
if (profile) {
this.profile = profile;
//TODO: initialize profile listener
if(this.unsub === null) {
this.initializeProfileListener();
}
}
}
resolve(true);
});
});
},
/**
*
* #param email email for login
* #param password password for login
*/
async signInEmailPassword(email: string, password: string) {
try {
const userCredential = await fbSignIn(email, password);
this.user = userCredential.user ? userCredential.user : null;
this.error = null;
return true;
} catch (error: any) {
console.log(typeof error.code);
console.log(error.code);
this.user = null;
this.error = error;
return false;
}
},
async logoutUser() {
try {
await fbSignOut();
this.user = null;
this.profile = null;
this.error = null;
if (this.unsub) this.unsub();
return true;
} catch (error: any) {
this.error = error;
return false;
}
},
async createEmailPasswordAccount(
email: string,
password: string,
userName: string,
refSource: string
) {
try {
const { user, profile } = await fbCreateAccount(
email,
password,
userName,
refSource
);
//set local store
this.user = user ? user : null;
this.profile = profile ? profile : null;
this.error = null;
//TODO: send email verification
return true;
} catch (error: any) {
this.user = null;
this.error = error;
return false;
}
},
initializeProfileListener() {
try {
if (this.user) {
const unsub = profileListener(
this.user?.uid,
async (profile: any) => {
if (profile) {
this.profile = profile;
}
}
);
this.unsub = unsub;
}
} catch (error) {
errorHandler(error as Error);
}
},
},
});
authHelper.ts
import { auth } from "#/firebase/firebase";
import {
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
signOut,
onAuthStateChanged,
updateProfile as updateAuthProfile,
} from "#firebase/auth";
import { Profile, setProfile, getProfile } from "./profileManager";
/**
* #param email
* #param password
* #param userName
* #param refSource #see profileManager
* #returns
*/
export const fbCreateAccount = async (
email: string,
password: string,
userName: string,
refSource: string
) => {
//DEBUG: creating a user works but throws an error.
const userCredential = await createUserWithEmailAndPassword(
auth,
email,
password
);
if (userCredential) {
//add username to fireauth profile
await updateAuthProfile(userCredential.user, { displayName: userName });
//create user profile data in firestore
let profile: Profile | undefined = new Profile(
userCredential.user.uid,
refSource
);
await setProfile(profile);
profile = await getProfile(userCredential.user.uid);
//TODO: errorHandling for setProfile and getProfile
return {
user: userCredential.user,
profile: profile,
};
} else {
return {
user: null,
profile: null,
};
}
};
/**
*
* #param email
* #param password
* #returns UserCredential {#link https://firebase.google.com/docs/reference/js/auth.usercredential.md?authuser=0#usercredential_interface}
*/
export const fbSignIn = async (email: string, password: string) => {
const userCredential = signInWithEmailAndPassword(auth, email, password);
//TODO: add call to add to profile signins array
return userCredential;
};
export const fbSignOut = async () => {
await signOut(auth);
return true;
};
/**
* #see {#link https://firebase.google.com/docs/reference/js/auth.md?authuser=0&hl=en#onauthstatechanged}
* #param callback contains either user or null
*/
export const fbAuthStateListener = (callback: any) => {
onAuthStateChanged(auth, (user) => {
if (user) {
//user is signed in
callback(user);
} else {
//user is signed out
callback(null);
}
});
};

What does kind undefined mean here? I cannot find any solution for this error

I am trying to split up my schema of GraphQL API into separate ones.
Having familiarized with a huge number of them I decided on this one below using "extend"
( I also wanted to use .graphql file extension for them but as I've got there is only one way to do that - it is with the use of webpack. I am not really good at it so I tried to make it work at first with .js files)
BUT: I cannot cope with this simple task because I either have TypeError: Cannot read property 'kind' of undefined or Invalid schema passed, or something else pops up...
What am I doing wrong here and what is the best practice/approach to split up and stitch the Schema?
Thanks in advance!
server.js
import express from "express";
import { ApolloServer, gql } from "apollo-server-express";
import { makeExecutableSchema } from 'graphql-tools';
import * as mongoClient from "./config";
import * as _ from 'lodash';
import { UserSchema, UserResolvers } from "./graphql.partials/user.api";
const port = process.env.PORT || 8080;
const RootSchema = gql`
type Query {
_empty: String
}
type Mutation {
_empty: String
}
`
const RootResolvers = {
};
const app = express();
const schema = makeExecutableSchema({
typeDefs: [RootSchema, UserSchema],
resolvers: _.merge(RootResolvers, UserResolvers)
});
const apolloServer = new ApolloServer({ schema });
apolloServer.applyMiddleware({ app });
app.listen({ port }, () => {
console.log(
`Server ready at http://localhost:${port}${apolloServer.graphqlPath}`
);
});
user schema
const { gql } = require("apollo-server-express");
import User from '../models/User';
export const typeDefs = gql`
extend type Query {
users: [User]
user(id: String): User
}
extend type Mutation {
addUser(email: String, password: String): User
deleteUser(id: String!): User
updateUser(user: UserInput): User
}
type User {
id: ID!
firstName: String
lastName: String
email: String!
password: String!
confirmed: Boolean
role: String
}
input UserInput {
id: ID!
firstName: String
lastName: String
email: String
password: String
confirmed: Boolean
role: String
}
`
export const UserResolvers = {
Query: {
users: async (obj, args, context, info) => {
try {
const res = await User.find();
return res;
} catch (err) {
return err.message;
}
},
user: async (obj, args, context, info) => {
try {
const res = User.findById(args['id']);
return res;
} catch (e) {
return e.message;
}
}
},
Mutation: {
addUser: async (obj, args, context, info) => {
try {
const res = await User.create(args);
return res;
} catch (err) {
return err.message;
}
},
deleteUser: async (obj, args, context, info) => {
try {
return User.findByIdAndDelete(args['id']);
} catch (e) {
return e.message;
}
},
updateUser: async (obj, args, context, info) => {
try {
return User.findByIdAndUpdate(args.user.id, args.user)
} catch (e) {
return e.message;
}
}
}
}
There is no export named UserSchema in your file. You have two named exports -- UserResolvers and typeDefs. As a result, when you attempt to import UserSchema, its value is undefined.

JWT not working on live server with Vuejs, Laravel

I am working on a SPA, I have used JWT authentication for a login system. on the local server its working fine, I mean when I click login I get the token etc and store on local storage and it redirects me to dashboard everything perfect.
but on a live server, I get the token but it doesn't store it on local storage.
I am completely lost. I tried everything but still. please help me with this.
it's my first time with the SPA so I am not sure what I am missing.
Login Method
login(){
User.login(this.form)
}
User.js
import Token from './Token'
import AppStorage from './AppStorage'
class User {
login(data) {
axios.post('/api/auth/login', data)
.then(res => this.responseAfterLogin(res))
.catch(error => console.log(error.response.data))
}
responseAfterLogin(res) {
const access_token = res.data.access_token
const username = res.data.user
if (Token.isValid(access_token)) {
AppStorage.store(username, access_token)
window.location = '/me/dashboard'
}
}
hasToken() {
const storedToken = AppStorage.getToken();
if (storedToken) {
return Token.isValid(storedToken) ? true : this.logout()
}
return false
}
loggedIn() {
return this.hasToken()
}
logout() {
AppStorage.clear()
window.location = '/me/login'
}
name() {
if (this.loggedIn()) {
return AppStorage.getUser()
}
}
id() {
if (this.loggedIn()) {
const payload = Token.payload(AppStorage.getToken())
return payload.sub
}
}
own(id) {
return this.id() == id
}
admin() {
return this.id() == 1
}
}
export default User = new User();
Token.js
class Token {
isValid(token){
const payload = this.payload(token);
if(payload){
return payload.iss == "http://127.0.0.1:8000/api/auth/login" ? true : false
}
return false
}
payload(token){
const payload = token.split('.')[1]
return this.decode(payload)
}
decode(payload){
return JSON.parse(atob(payload))
}
}
export default Token = new Token();
AppStorage.js
class AppStorage {
storeToken (token) {
localStorage.setItem('token', token);
}
storeUser (user) {
localStorage.setItem('user', user);
}
store (user, token) {
this.storeToken(token)
this.storeUser(user)
}
clear () {
localStorage.removeItem('token')
localStorage.removeItem('user')
}
getToken () {
return localStorage.getItem('token')
}
getUser () {
return localStorage.getItem('user')
}
}
export default AppStorage = new AppStorage()
Thanks