I am building a Token authentication in React and have an issue. Below there are two lines of the code:
when I use proper code according to eslint:
const { token } = response.data.token;
it returns no error, but I cannot see the access_token, it is unidentified in local storage
when I use not proper code according to eslint:
const token=response.data.token;
then it shows me the actual code in local storage.
What is happening?
const { token } = response.data.token; is sugar for const token = response.data.token.token;, which is not what you want.
The following will be equivalent to your second snippet:
const { token } = response.data;
Related
I'm trying to verify a JWT token in a middleware on Next.js (v12.3.2) but am getting errors with jsonwebtoken (following the docs here: https://clerk.dev/docs/request-authentication/validate-session-tokens) because it requires a Node environment.
I've tried using jose instead with the CLERK_JWT_KEY for Clerk.dev but I keep getting a [Error: Key info doesn't have required parameters] error. For reference, this is what my code looks like:
export const decodeAndVerifyToken = async (
getToken: ServerGetToken
): Promise<JWTPayload | undefined> => {
// initialize a variable for the token
let token: string | null;
try {
// get the token using metadata template, which should return
// a 'publicMetadata' object containing an 'isAdmin' value
token = await getToken({ template: "metadata" });
} catch (err) {
// if we had an error getting the token, return undefined
return undefined;
}
// if no token is found, then short-circuit to undefined
if (!token) {
return undefined;
}
// split the jwt key to 64-bit lines
const splitPem = process.env.CLERK_JWT_KEY?.match(/.{1,64}/g) ?? [];
// combine into a public key format
const publicKey =
"-----BEGIN PUBLIC KEY-----\n" +
splitPem.join("\n") +
"\n-----END PUBLIC KEY-----";
//
const test = await importSPKI(publicKey, "ES256").catch((err) =>
console.log(err)
);
};
I've also tried directly calling const decoded = await jwtVerify(token, publicKey); but that also produces errors.
Anyone know how to address?
As per the example key in clerk.dev these are RSA keys you're importing. That key is not a valid key for ES256 that you pass to importSPKI.
I suspect the algorithm will be RS256 but to be sure inspect the token's header to confirm.
Looking at the docs they do have a JWKs endpoint so this might be a wholelot easier for you.
import * as jose from 'jose'
const JWKS = jose.createRemoteJWKSet(new URL('https://<YOUR_FRONTEND_API>/.well-known/jwks.json'))
const { payload, protectedHeader } = await jose.jwtVerify(token, JWKS)
jsonwebtoken doesn't work in middleware because it depends on crypto which is not available on edge functions.
Like you suggested, you can use jose. Make sure to use the key marked "PEM public key" from the Clerk dashboard (hidden under the advanced menu - it should start with -----BEGIN PUBLIC KEY-----).
Then, just pass it is to importSPKI as follows:
const { userId, getToken } = getAuth(request)
const token = await getToken({ template: "candor" })
const publicKey = await jose.importSPKI(process.env.CLERK_PUBLIC_KEY, "RS256")
const { payload } = await jose.jwtVerify(token, publicKey)
We are using Auth0 SDK and trying to include a custom field during the login process. The purpose is to be able to inject dynamic params into payload from rules. Client is a react.js app and we use #auth0/auth0-react library. Noticed that getAccessTokenSilently is extensible to include custom params:
const { getAccessTokenSilently } = useAuth0();
const accessToken = await getAccessTokenSilently({
audience: 'myaudience',
scope: 'read:read_users',
'foo': 'bar'
});
The question is, how we get access to foo=bar from inside the rules triggers?
Thanks
To access custom fields in access token, you need Auht0 Auth pipeline rules. To trigger rule you need to pass additional option ignoreCache: true i.e.
const { getAccessTokenSilently } = useAuth0();
const accessToken = await getAccessTokenSilently({
ignoreCache: true,
audience: 'myaudience',
scope: 'read:read_users',
customClaim1: 'MyUserId',
customClaim2: 'MyPreference'
});
In Auht0 Auth pipeline rules, access these custom fields in context.request.query and assign in accessToken as shown below:
function (user, context, callback) {
const namespace = 'http://rules/customClaims';
const customClaim1 = (context.request.query || {}).customClaim1;
const customClaim2 = (context.request.query || {}).customClaim2;
let accessTokenClaims = context.accessToken || {};
accessTokenClaims[`${namespace}/customClaim1`] = customClaim1;
accessTokenClaims[`${namespace}/customClaim2`] = customClaim2;
context.accessToken = accessTokenClaims;
return callback(null, user, context);
}
You can pass and access any custom field in rule. In your case simply replace customClaim1: 'MyUserId' with 'foo': 'bar'
In my web application, a user signs in and a token is created. Now i want to add a claim to the payload of this token: a boolean value for being an admin or not.
I've searched around the web, but i can't seem to find how to implement this in a Mean stack application.
My goal is when a user signs in, i can check if the user is an admin using the payload of the token. Then i can lock specific parts of my application so they are only accessible for admins.
This is the login function in the service
function logIn(user) {
return $http.post('/api/users/login', user, {
headers: {
Authorization: 'Bearer ' + getToken()
}
}).success(function(data) {
saveToken(data.token);
}).error(function(err){
return err;
});
}
These are the functions to save the token and get the token from local storage:
function saveToken(token) {
$window.localStorage['ptlab-app-token'] = token;
}
function getToken() {
return $window.localStorage['ptlab-app-token'];
}
And this is the function i use to check if a user is logged in in order to unlock specific parts of the application:
function isLoggedIn() {
var token = getToken();
if (token) {
var payload = angular.fromJson($window.atob(token.split('.')[1]));
return payload.exp > Date.now() / 1000;
} else {
return false;
}
}
So i want to do sort of the same as isLoggedIn with the admin check. But i can't seem to figure out how to customize the payload of the jwt token and add a claim to the payload. With a claim called "admin", i can easily check if the user is and admin without having to access the database.
Has anybody got any suggestions ?
You can adjust the payload for the token upon its creation (in Node.js), so you can simply add a isAdmin boolean. Afterwards you can decode the token and retrieve the value of isAdmin to see if the user is an admin or not.
Token creation:
const payload = {
id: user._id,
isAdmin: user.isAdmin,
};
const token = jwt.sign(payload, "superSecret", {
expiresIn: 86400,
});
Token decoding:
const decoded = await jwt.verify(token, "superSecret");
const isAdmin = decoded.isAdmin;
you may simply go to JWT.io decode your token and find the content and even you can send the isAdmin Key and value in token decode and verify it on client side in node it is jwt.decode and same in angular application.
I'm trying to use Google Cloud Print(GCP) API, but I can't make it works.
Maybe I've understood bad the workflow because is the first time I'm using the google api, please help me to understand how to make it works.
Initial considerations:
I'm trying to implement it in reactJS, but It is indifferent because the logic to make GCP works is independent of the technology. Then you also can help me understand the workflow.
What exactly I want:
To make my first test, I am looking to get all information about my printer.
What I did:
I created a project in: https://console.developers.google.com
Inside the project created, I created a credential:
create credentials -> OAuth client ID
And I chose Application type: Web, and also configure the restrictions to source and redirection to my localhost.
Manually in https://www.google.com/cloudprint, I added my printer, I made a test printing a PDF and was OK.
I created a project in reactJS to get the information of my printer I've added.
Component:
Explanation:
I'm using a component react-google-login to obtain easily the user accessToken: https://github.com/anthonyjgrove/react-google-login
This component only obtains the access token and save it in localStorage, in a variable called googleToken and it draws a button to call a function to obtain the information about the printer.
code:
import React, { Component } from 'react'
import GoogleLogin from 'react-google-login';
import { connect } from 'react-redux'
import { getPrinters } from '../actions/settings'
class Setting extends Component {
responseGoogle(response) {
const accessToken = response.accessToken
localStorage.setItem('googleToken', accessToken)
}
render() {
return (
<div>
<GoogleLogin
clientId="CLIENT_ID_REMOVED_INTENTIONALLY.apps.googleusercontent.com"
buttonText="Login"
onSuccess={this.responseGoogle}
onFailure={this.responseGoogle}
/>
<button
onClick = {() => {
this.props.getPrinters()
}}
>test printer</button>
</div>
)
}
}
const mapStateToProps = state => {
return {
state: state
}
}
const mapDispatchToProps = dispatch => {
return {
getPrinters() {
dispatch(getPrinters())
}
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Setting)
Action or Function to get information printer:
Explanation:
I'm passing the parameter printerid to get information about that printer.
In authorization, I'm using OAuth ... because in the documentation says that(second paragraph).: https://developers.google.com/cloud-print/docs/appInterfaces
The next two headers I wrote it because I tried solutions as:
Google Cloud Print API: User credentials required
Google Cloud Print User credentials required
code:
import axios from 'axios'
axios.defaults.headers.common['Authorization'] = 'OAuth ' + localStorage.getItem('googleToken')
axios.defaults.headers.common['scope'] = 'https://www.googleapis.com/auth/cloudprint'
axios.defaults.headers.common['X-CloudPrint-Proxy'] = 'printingTest'
const getPrinters = () => {
return () => {
return axios.get('https://www.google.com/cloudprint/printer'
, {
params: {
printeid: 'PRINTER_ID_REMOVED_INTENTIONALLY'
}
}
)
.then(response => {
console.log('response of google cloud print')
console.log(response)
})
}
}
export { getPrinters }
Error:
After all explained before, I got the next error:
User credentials required
Error 403
Note:
I'm using CORS plugin by recommendation of:
Chrome extensions for silent print?
because initially, I had cors error.
Any suggestion or recommendation would be very useful, thanks.
I've resolved my problem, my main problem about User Credential required were because I was using the incorrect access token and It was because I was getting the access token incorrectly.
I'm going to explain my whole solution because there are few examples of codes with this API.
Solutions:
The steps described were Ok until the fourth step where I used the external component react-google-login to trying to get the access token, instead I used googleapis module: Link Github googleapis
Also to avoid CORS problem(and not use CORS chrome plugin) I wrote the requests to Google API in server side.(NODEJS)
I had also a problem in the frontend when I tried to generate a popup to give permission for printer(problems about CORS), my solution was to use this very simple module for authentication: Link Github oauth-open
General scheme:
Explanation:
Knowing I have all data described in my question post(until the third step).
Authentication:
The next step in getting a URL and use it to the user can authenticate.
As I said before I used the module oauth-open in the frontend to generate the popup and only this module need the URL. To get the URL in the backend I used the endpoint /googleurl, where here I used the method generateAuthUrl of the module googleapis to generate the URL.
After that In the frontend, I got the authentication_code(that returned the module oauth-open), I send It to my endpoint /googletoken and here I process the authentication_code to generate access token, refresh token and expiration date with the method getToken of the module googleapis. Finally, these data are stored in the database.
Print:
For print, since the frontend, I send what data I need send to the printer. I used my endpoint /print
In the backend endpoint, my logic was the next:
Recover tokens and expiration date from database, with the expiration date check if the token has expired, and if It has already expired then gets another token and replace the old access token with the new one, replacing also with the new expiration date, to obtain this new data only is necessary call to method refreshAccessToken of module googleapis.Note: the refresh token never expires.
After having the access token updated, use it to send data to the printer with Google route(.../submit)
Code:
All the next codes are in only 1 file
Some data as validation, static variables, error handler, etc, has been removed to better understanding.
Route get URL authentication.
const express = require('express');
const google = require('googleapis');
const router = express.Router();
var OAuth2 = google.auth.OAuth2;
const redirect_url = 'http://localhost:3001/setting'; //Your redirect URL
var oauth2Client = new OAuth2(
'CLIENT ID', //Replace it with your client id
'CLIEND SECRET', //Replace it with your client secret
redirect_url
);
var url = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: 'https://www.googleapis.com/auth/cloudprint'
});
router.get('/googleurl', (req, res) => {
return res.status(200).send({
result: { googleURLToken: url }
});
});
To get tokens using the authentication code and save these in the database.
const Setting = require('../models/setting'); // My model(Mongoose)
router.post('/googletoken', (req, res) => {
oauth2Client.getToken(req.body.code, function (err, tokens) {
oauth2Client.credentials = tokens;
// If refresh token exits save it
// because the refresh token it returned only 1 time! IMPORTANT
if (tokens.hasOwnProperty('refresh_token')) {
let setting = new Setting();
setting.refreshTokenGoogle = tokens.refresh_token;
setting.expirationTokenGoogle = tokens.expiry_date;
setting.tokenGoogle = tokens.access_token;
setting.save()
.then((settingCreated) => {
return res.status(200).send({
message: 'OK'
});
})
}
});
});
To print
const axios = require('axios');
const moment = require('moment');
router.post('/print',async (req, res) => {
const tickeProperties = {
'version': '1.0',
'print': {
'vendor_ticket_item': [],
'color': { 'type': 'STANDARD_MONOCHROME' },
'copies': { 'copies': 1 }
}
};
const accessToken = await getTokenGoogleUpdated();
axios.get(
'https://www.google.com/cloudprint/submit',
{
params: {
printerid : printerID, // Replace by your printer ID
title: 'title printer',
ticket: tickeProperties,
content : 'print this text of example!!!',
contentType: 'text/plain'
},
headers: {
'Authorization': 'Bearer ' + accessToken
}
}
)
.then(response => {
return res.status(200).send({
result: response.data
});
})
}
);
async function getTokenGoogleUpdated() {
return await Setting.find({})
.then(async setting => {
const refreshTokenGoogle = setting[0].refreshTokenGoogle;
const expirationTokenGoogle = setting[0].expirationTokenGoogle;
const tokenGoogle = setting[0].tokenGoogle;
const dateToday = new Date();
// 1 minute forward to avoid exact time
const dateTodayPlus1Minute = moment(dateToday).add(1, 'm').toDate();
const dateExpiration = new Date(expirationTokenGoogle);
// Case date expiration, get new token
if (dateExpiration < dateTodayPlus1Minute) {
console.log('Updating access token');
oauth2Client.credentials['refresh_token'] = refreshTokenGoogle;
return await oauth2Client.refreshAccessToken( async function(err, tokens) {
// Save new token and new expiration
setting[0].expirationTokenGoogle = tokens.expiry_date;
setting[0].tokenGoogle = tokens.access_token;
await setting[0].save();
return tokens.access_token;
});
} else {
console.log('Using old access token');
return tokenGoogle;
}
})
.catch(err => {
console.log(err);
});
}
I hope It helps you if you want to use Google Cloud Print to not waste a lot of time as I did.
The important part there is a scope https://www.googleapis.com/auth/cloudprint which is not obvious and took one day for me to figure out.
In the following code fragment, I get the token information of the user who is logged in.
async getToken(accessToken){
try {
let token = await AsyncStorage.getItem(accessToken);
console.log("getToken: " + token);
} catch (error) {
console.log("something went wrong gettoken")
}
},
This token information returns to me like this.
{"auth_token" : "2jk34k23jh42lklasdfqwpoerp12134saqwoswc"}
I just need to access the token information on the 2nd parameter. How can i do that?
You can access it with
token["auth_token"]