This is weird for me.
According this document i made my Auth SchemaDirective like this:
class RequireAuthDirective extends SchemaDirectiveVisitor {
visitFieldDefinition(field) {
const { resolve = defaultFieldResolver } = field;
field.resolve = async (...args) => {
let [, params, { req, res, connection }] = args;
let { user } = req || connection.context;
if (!!user) {
return await resolve.apply(this, args);
} else {
throw new AuthenticationError('You must be signed in to view this resource.');
}
};
}
}
This works fine in every Query, Mutation, Subscription
But when i use an external async method to check authorization like this:
class RequireAuthDirective extends SchemaDirectiveVisitor {
visitFieldDefinition(field) {
const { resolve = defaultFieldResolver } = field;
field.resolve = async (...args) => {
let [, params, { req, res, connection }] = args;
let { user } = req || connection.context;
if (!!user) {
if (await isAllowed(user.id, field.name, params)) return await resolve.apply(this, args); // ChkPoint
throw new AuthenticationError('You are not authorized to access this resource.');
} else {
throw new AuthenticationError('You must be signed in to access this resource.');
}
};
}
}
This works fine in Queries and Mutations but not in subscriptions!
Note: if i remove the ChkPoint condition ( Which is returning true ) it will works in subscription.
Related
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.
I deployed my solidity smart contract in polygon testnet(mumbai network).
I tested my smart contract works well in my react frontend except one payable function.
function getLotto() payable external {
require(msg.value == 100000000000000000, "Pay 0.1MATIC");
...
}
here's my frontend code. it will be called when some button is pressed.
of course, I set the signer and abi in the contract.
const handler = () => {
const options = {
gasPrice: 800000,
value: ethers.BigNumber.from("100000000000000000"),
};
await myContract.getLotto(options);
}
I also tried this code but it also didn't work.
value: ethers.utils.parseEther("0.1")
here's the error message.
---edited------
export class MarioNft {
constructor(rpc, contractAddr, abi) {
this.rpc = rpc;
this.contractAddr = contractAddr;
this.abi = abi;
this.isSigned = false;
this.provider = new ethers.providers.JsonRpcProvider(rpc);
this.contract = new ethers.Contract(contractAddr, abi, this.provider);
this.baseUri = null;
}
...
setContractWithSigner(signer) {
this.contract = new ethers.Contract(this.contractAddr, this.abi, signer);
this.isSigned = true;
}
async lotto() {
if (this.isSigned) {
const res = await this.contract.getLotto();
console.log("lotto res: ", res); /////
} else {
///////
console.log("from marioNft class: signer is false");
}
}
}
const lottoHandler = async () => {
if (metaProvider === null) {
alert("Need to connect to MetaMask");
} else {
if (!marioNft.checkIsSigned()) {
marioNft.setContractWithSigner(metaSigner);
}
const options = {
gasPrice: 800000,
value: ethers.BigNumber.from("100000000000000000"),
};
await marioNft.lotto(options);
};
-------------edited2-----------------
const updateEthers = () => {
let tempProvider = new ethers.providers.Web3Provider(window.ethereum);
setMetaProvider(tempProvider);
// this tempSigner will be used in marioNft.setContractWithSigner()
let tempSigner = tempProvider.getSigner();
setMetaSigner(tempSigner);
};
when I print tempSigner
Your async lotto function inside of the MarioNFT class does not pass on the options object:
async lotto() {
if (this.isSigned) {
const res = await this.contract.getLotto();
console.log("lotto res: ", res); /////
} else {
///////
console.log("from marioNft class: signer is false");
}
}
So it should be:
async lotto(options) {
if (this.isSigned) {
const res = await this.contract.getLotto(options);
console.log("lotto res: ", res); /////
} else {
///////
console.log("from marioNft class: signer is false");
}
}
I'm getting this error in a VueJS application: Uncaught (in promise) Error: Navigation cancelled from "/" to "/dashboard" with a new navigation. Its been documents in SO before
here and I've seen another article explaining it's due to the OAuth library I am using, making 2 calls thus causing a redirect.
I can't figure out how to suppress the error in my situation, the other answers use router.push(), i'm using a function in beforeEnter.
I've tried using: Try/Accept and catch (but not sure where to put them):
try {
next("/dashboard");
} catch (err) {
throw new Error(`Problem handling something: ${err}.`);
}
How do I suppress the error?
My Router:
export default new VueRouter({
mode: "history",
base: process.env.BASE_URL,
routes: [
{
path: "/",
name: "Landing",
component: Landing,
meta: {
title: "Landing",
},
beforeEnter: loginRedirectDashboard,
},
})
Router Guard (beforeEnter)
function loginRedirectDashboard(to, from, next) {
const authService = getInstance();
authService.$watch("loading", (loading) => {
if (loading === false) {
if (to.path === "/") {
next("/dashboard");
} else {
next();
}
}
});
}
Update:
instance() code
import Vue from "vue";
import createAuth0Client from "#auth0/auth0-spa-js";
/** Define a default action to perform after authentication */
const DEFAULT_REDIRECT_CALLBACK = () =>
window.history.replaceState({}, document.title, window.location.pathname);
let instance;
/** Returns the current instance of the SDK */
export const getInstance = () => instance;
/** Creates an instance of the Auth0 SDK. If one has already been created, it returns that instance */
export const useAuth0 = ({
onRedirectCallback = DEFAULT_REDIRECT_CALLBACK,
redirectUri = window.location.origin,
...options
}) => {
if (instance) return instance;
// The 'instance' is simply a Vue object
instance = new Vue({
data() {
return {
loading: true,
isAuthenticated: false,
user: {},
auth0Client: null,
popupOpen: false,
error: null,
permission: null,
};
},
methods: {
/** Authenticates the user using a popup window */
async loginWithPopup(options, config) {
this.popupOpen = true;
try {
await this.auth0Client.loginWithPopup(options, config);
this.user = await this.getUserWithMeta(); //await this.auth0Client.getUser();
this.isAuthenticated = await this.auth0Client.isAuthenticated();
this.error = null;
this.permission = await this.permissionLevels();
} catch (e) {
this.error = e;
// eslint-disable-next-line
console.error(e);
} finally {
this.popupOpen = false;
}
this.user = await this.auth0Client.getUser();
this.isAuthenticated = true;
},
/** Handles the callback when logging in using a redirect */
async handleRedirectCallback() {
this.loading = true;
try {
await this.auth0Client.handleRedirectCallback();
this.user = await this.getUserWithMeta(); //await this.auth0Client.getUser();
this.isAuthenticated = true;
this.error = null;
this.permission = await this.permissionLevels();
} catch (e) {
this.error = e;
} finally {
this.loading = false;
}
},
/** Authenticates the user using the redirect method */
loginWithRedirect(o) {
return this.auth0Client.loginWithRedirect(o);
},
/** Returns all the claims present in the ID token */
getIdTokenClaims(o) {
return this.auth0Client.getIdTokenClaims(o);
},
/** Returns the access token. If the token is invalid or missing, a new one is retrieved */
getTokenSilently(o) {
return this.auth0Client.getTokenSilently(o);
},
/** Gets the access token using a popup window */
async getUserWithMeta() {
var userObject = await this.auth0Client.getUser();
if (userObject != null) {
var namespace = '********';
var extractUserMeta = userObject[namespace];
delete userObject[namespace];
userObject.userInfo = extractUserMeta;
return userObject;
}
},
async permissionLevels(o) {
if (this.isAuthenticated) {
const jwtToken = await this.auth0Client.getTokenSilently(o);
var base64Url = jwtToken.split(".")[1];
var base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
var jsonPayload = decodeURIComponent(
atob(base64)
.split("")
.map(function(c) {
return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
})
.join("")
);
return JSON.parse(jsonPayload).permissions;
} else {
return false;
}
},
getTokenWithPopup(o) {
return this.auth0Client.getTokenWithPopup(o);
},
/** Logs the user out and removes their session on the authorization server */
logout(o) {
delete localStorage.authToken;
return this.auth0Client.logout(o);
},
},
/** Use this lifecycle method to instantiate the SDK client */
async created() {
// Create a new instance of the SDK client using members of the given options object
this.auth0Client = await createAuth0Client({
...options,
client_id: options.clientId,
redirect_uri: redirectUri,
});
try {
// If the user is returning to the app after authentication..
if (
window.location.search.includes("code=") &&
window.location.search.includes("state=")
) {
// handle the redirect and retrieve tokens
const { appState } = await this.auth0Client.handleRedirectCallback();
this.error = null;
// Notify subscribers that the redirect callback has happened, passing the appState
// (useful for retrieving any pre-authentication state)
onRedirectCallback(appState);
}
} catch (e) {
this.error = e;
} finally {
// Initialize our internal authentication state
this.isAuthenticated = await this.auth0Client.isAuthenticated();
this.user = await this.getUserWithMeta(); //await this.auth0Client.getUser();
this.loading = false;
this.permission = await this.permissionLevels();
}
},
});
return instance;
};
// Create a simple Vue plugin to expose the wrapper object throughout the application
export const Auth0Plugin = {
install(Vue, options) {
Vue.prototype.$auth = useAuth0(options);
},
};
I am working with express server and trying to read data from file and return it to different function.
My code structure looks like this:
async function getUser(req) {
fs.readFile(cfg.fileDefaults, (err, defaults) => {
do something
return user;
}
}
module.exports = {getUser}
In controller I call that function
static getTable(req, res, next) {
async function search() {
user = await getUser(req); //return undefined
getUser(req).then((a) => {
console.log(a); //second try, return undefined
})
}
search();
}
How should I call it correctly?
const fs = require('fs')
function getUser(req) {
return new Promise((resolve, reject) => {
fs.readFile(cfg.fileDefaults, (err, defaults) => {
//do something
const user = { hellow: 'world' }
resolve(user)
})
})
}
module.exports = { getUser }
In controller
static getTable(req, res, next) {
async function search() {
user = await getUser(req); // return { hellow: 'world' }
res.end(JSON.stringify(user, null, ' '))
}
search().catch((err) => {
console.log('err',err)
res.status(500)
res.end('error')
})
}
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