Cypress request by exec curl - api

I am writing test cases for tutorial using https://juice-shop.herokuapp.com/#/
I am trying to add user using api.
I wanted to add a user with curl, but it fails. It responds with success but new user is missing an email that is used to log in.
I can't find my mistake
import { faker } from '#faker-js/faker';
const un = faker.internet.userName();
const domain = "#test.com";
const password = "qwerty123";
const api = "http://localhost:3000/api/Users/";
const sQuestion = "Your eldest siblings middle name?";
const sAnswer = "Joe";
describe('Login test cases suite', () => {
describe('Login test cases - basic login cases', () => {
it('New user - created by api', () => {
_apiRegisterNewUser(`${un}_api${domain}`, password);
});
it('New user - created by curl', () => {
_curlRegisterNewUser(`${un}_curl${domain}`, password);
});
});
});
export function _apiRegisterNewUser(email, password){
cy.request(
{
method: 'POST',
url: api,
body: {
"email": email,
"password": password,
"securityQuestion": {
"id": "",
"question": sQuestion,
"createdAt": `${new Date().toISOString()}`,
"updatedAt": `${new Date().toISOString()}`,
},
"securityAnswer": sAnswer
}
}
)
.then((response) => cy.log(JSON.stringify(response)));
}
export function _curlRegisterNewUser(email, password){
const com =
`curl --request POST ${api}
--header "Content-Type: application/json"
--data-raw "{\"email\":\"${email}\",\"password\":\"${password}\",\"passwordRepeat\":\"${password}\",
\"securityQuestion\":{\"id\":\"1\",\"question\":\"${sQuestion}\",\"createdAt\":\"\",\"updatedAt\":\"\",},
\"securityAnswer\":\"${sAnswer}\"}"`
cy.log(com);
cy.exec(com).then((obj) => cy.log(obj.stdout));
}

Related

I have the required fields for user login how to bypass the nextauth login page and perform the authorize method

I create a custom user registration because nextauth does not support registration. Everything works correctly but I do not know how after a successful registration of the user immediately log him in the credentials that he gave during registration.
As far as I can see, the signIn method from nextauth does not allow any credentials to be passed on to it. Redirects only to the login subpage.
I also did not find any endpoint that provides nextauth to which I can pass parameters so as to log in the user.
In fact, it is enough to call the authorize method that is in nextauth, unfortunately, there is no possibility to export it or there is no way to call it from the call to api level and it is a pity because with it I could log in the user.
User flow
User registers if the registration is successful, he is immediately logged in credentials that he provided during registration
My registration
async function handleRegister(
username: string,
email: string,
password: string
) {
const registerUser = await fetch(
`${process.env.API_URL}`,
{
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({
username,
email,
password,
}),
}
);
const registerResponse = await registerUser.json();
if (registerResponse.user) {
// TODO: Login with NextAuth
}
}
[...nextauth].ts
export default NextAuth({
providers: [
CredentialsProvider({
name: "Credentials",
credentials: {
identifier: {
label: "Email or Username",
type: "text",
placeholder: "jsmith",
},
password: { label: "Password", type: "password" },
},
async authorize(credentials, req) {
const res = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/api/auth/local`,
{
method: "POST",
body: JSON.stringify(credentials),
headers: { "Content-Type": "application/json" },
}
);
const user = await res.json();
if (res.ok && user) {
return user;
}
return null;
},
}),
],
session: {
strategy: "jwt",
},
jwt: {
maxAge: 60,
encode: async ({ secret, token }) => {
const encodedToken = jsonwebtoken.sign(token!, secret, {
algorithm: "HS256",
});
return encodedToken;
},
decode: async ({ secret, token }) => {
const decodedToken = jsonwebtoken.verify(token!, secret, {
algorithms: ["HS256"],
});
return decodedToken as JWT;
},
},
callbacks: {
jwt: async (token, user, account) => {
const isSignIn = user ? true : false;
if (isSignIn) {
token.jwt = user.jwt;
token.id = user.user.id;
token.name = user.user.username;
token.role = user.user.user_role;
token.email = user.user.email;
}
return Promise.resolve(token);
},
session: async ({ session, token }) => {
if (session.user) {
session.user.id = token.sub!;
}
return session;
},
},
secret: process.env.JWT_SECRET,
});
You need to login with credentials e.g
const response: SignInResponse | undefined = await signIn(
"credentials",
{
redirect: false,
email: "example#user.com",
password: "12345678",
}
);

Vue-Authenticate for Github Login

I have used the Vue-Authenticate plugin for the social login and this is my configuration for the Github Login
providers: {
github: {
clientId: 'my Id',
redirectUri: 'https://' + process.env.PROJECT_DOMAIN,
responseType: 'token',
authorizationEndpoint: 'https://github.com/login/oauth/authorize',
},
},
And in the method through which is calling the method after authentication is
authenticate(provider) {
const this_ = this
this.$auth.authenticate(provider).then(function () {
const token = this_.$auth.getToken()
if (provider === 'github') {
const options = {
headers: {
Authorization: 'token ' + token,
},
}
this_.$http
.post('https://api.github.com/user', options)
.then(function (response) {
if (response.status === 200) {
const { email, name, picture } = response.data
const data = {
email,
name,
image: picture.data.url,
}
this_.createOAuth2User(data, provider)
}
})
.catch((error) => {
console.log(error)
})
}
})
},
I am able to receive an access token after successful authentication but when I try to use that token to access the user details and hit the https://api.github.com/user API I am getting 401 error. So is there something I am missing while authentication with github?

Send silent request to refresh tokens

I am using Vue.js in frontend and JWT based authentication system. I have refresh tokens and access tokens. Access tokens have short amount of expiration time whereas refresh tokens have way longer. I want to send a request to server to refresh user's access token silently. I know when the access token will be expired. I want to refresh it 1 minute, or something, before it expires. How can I implement this? I thought to do it with putting a counter to my root component but I have no an exact solution. Thanks.
I have a similar problem as you do and found this Vue JWT Auth in the same search that pulled your answer. Implementation was a little more challenging than I had originally anticipated.
My application needs to have the JWT tokens refresh on page reloads and immediately before API calls. To do this I use axios to consume the APIs, allowing the use of an interceptor to check the validity of the tokens. To keep the UX smooth, I use the vuex store to maintain the tokens, escalating to localStorage, and then to making an external request for new tokens if each previous stage was not successful.
The components outside of the store look like:
src/utils/apiAxios.js: used to consume APIs
import axios from 'axios'
import config from '../../config'
import store from '../store'
const apiAxios = axios.create({
baseURL: `${config.dev.apiURL}api/`,
timeout: 1000,
headers: {'Content-Type': 'application/json'}
})
// before any API call make sure that the access token is good
apiAxios.interceptors.request.use(function () {
store.dispatch('isLoggedIn')
})
export default apiAxios
To src/main.js added these lines:
import store from './store'
router.beforeEach((to, from, next) => {
let publicPages = ['/auth/login/', '/auth/register/']
let authRequired = !publicPages.includes(to.path)
let loggedIn = store.dispatch('isLoggedIn')
if (authRequired && !loggedIn) {
return next('/auth/login/')
}
next()
})
src/store/index.js:
import Vue from 'vue'
import Vuex from 'vuex'
import createLogger from 'vuex/dist/logger'
import auth from './modules/auth'
Vue.use(Vuex)
const debug = process.env.NODE_ENV !== 'production'
export default new Vuex.Store({
modules: {
auth
},
strict: debug,
plugins: debug ? [createLogger()] : []
})
src/store/modules/auth.js:
import axios from 'axios'
import jwtDecode from 'jwt-decode'
import router from '../../utils/router'
import apiAxios from '../../utils/apiAxios'
import config from '../../../config'
export default {
state: {
authStatus: '',
jwt: {
refresh: '',
access: ''
},
endpoints: {
obtainJWT: config.dev.apiURL + 'auth/',
refreshJWT: config.dev.apiURL + 'auth/refresh/',
registerJWT: config.dev.apiURL + 'auth/register/',
revokeJWT: config.dev.apiURL + 'auth/revoke/',
verifyJWT: config.dev.apiURL + 'auth/verify/'
}
},
mutations: {
UPDATE_TOKEN (state, newToken) {
apiAxios.defaults.headers.common['Authorization'] = `Bearer ${newToken.access}`
localStorage.setItem('jwtAccess', newToken.access)
localStorage.setItem('jwtRefresh', newToken.refresh)
state.authStatus = 'success'
state.jwt = newToken
},
UPDATE_STATUS (state, statusUpdate) {
state.authStatus = statusUpdate
},
REVOKE_TOKEN (state) {
delete apiAxios.defaults.headers.common['Authorization']
localStorage.removeItem('jwtAccess')
localStorage.removeItem('jwtRefresh')
state.authStatus = ''
state.jwt = {
refresh: '',
access: ''
}
}
},
getters: {
authStatus: state => state.authStatus,
isLoggedIn: getters => {
// quick check of the state
return getters.authStatus === 'success'
}
},
actions: {
login ({ state, commit }, { email, password }) {
axios({
url: state.endpoints.obtainJWT,
method: 'POST',
data: {
email: email,
password: password
},
headers: {'Content-Type': 'application/json'}
})
.then((response) => {
commit('UPDATE_TOKEN', response.data)
})
.catch((error) => {
commit('UPDATE_STATUS', error)
console.log(error)
})
},
register ({ state, commit }, { email, password, firstName, lastName }) {
axios({
url: state.endpoints.registerJWT,
method: 'POST',
data: {
email: email,
password: password,
first_name: firstName,
last_name: lastName
},
headers: {'Content-Type': 'application/json'}
})
.then((response) => {
commit('UPDATE_TOKEN', response.data)
})
.catch((error) => {
commit('UPDATE_STATUS', error)
console.log(error)
})
},
logout ({ state, commit }) {
let refresh = localStorage.getItem('jwtRefresh')
axios({
url: state.endpoints.revokeJWT,
method: 'POST',
data: { token: refresh },
headers: {'Content-Type': 'application/json'}
})
.then(commit('REVOKE_TOKEN'))
.catch((error) => {
commit('UPDATE_STATUS', error)
console.log(error)
})
},
refreshTokens ({ state, commit }) {
let refresh = localStorage.getItem('jwtRefresh')
axios({
url: state.endpoints.refreshJWT,
method: 'POST',
data: {refresh: refresh},
headers: {'Content-Type': 'application/json'}
})
.then((response) => {
this.commit('UPDATE_TOKEN', response.data)
})
.catch((error) => {
commit('UPDATE_STATUS', error)
console.log(error)
})
},
verifyToken ({ state, commit, dispatch, getters }) {
let refresh = localStorage.getItem('jwtRefresh')
if (refresh) {
axios({
url: state.endpoints.verifyJWT,
method: 'POST',
data: {token: refresh},
headers: {'Content-Type': 'application/json'}
})
.then(() => {
// restore vuex state if it was lost due to a page reload
if (getters.authStatus !== 'success') {
dispatch('refreshTokens')
}
})
.catch((error) => {
commit('UPDATE_STATUS', error)
console.log(error)
})
return true
} else {
// if the token is not valid remove the local data and prompt user to login
commit('REVOKE_TOKEN')
router.push('/auth/login/')
return false
}
},
checkAccessTokenExpiry ({ state, getters, dispatch }) {
// inspect the store access token's expiration
if (getters.isLoggedIn) {
let access = jwtDecode(state.jwt.access)
let nowInSecs = Date.now() / 1000
let isExpiring = (access.exp - nowInSecs) < 30
// if the access token is about to expire
if (isExpiring) {
dispatch('refreshTokens')
}
}
},
refreshAccessToken ({ dispatch }) {
/*
* Check to see if the server thinks the refresh token is valid.
* This method assumes that the page has been refreshed and uses the
* #verifyToken method to reset the vuex state.
*/
if (dispatch('verifyToken')) {
dispatch('checkAccessTokenExpiry')
}
},
isLoggedIn ({ getters, dispatch }) {
/*
* This method reports if the user has active and valid credentials
* It first checks to see if there is a refresh token in local storage
* To minimize calls it checks the store to see if the access token is
* still valid and will refresh it otherwise.
*
* #isLoggedIn is used by the axios interceptor and the router to
* ensure that the tokens in the vuex store and the axios Authentication
* header are valid for page reloads (router) and api calls (interceptor).
*/
let refresh = localStorage.getItem('jwtRefresh')
if (refresh) {
if (getters.isLoggedIn) {
dispatch('checkAccessTokenExpiry')
} else {
dispatch('refreshAccessToken')
}
return getters.isLoggedIn
}
return false
}
}
}
I'm using django for my backend and django-rest-framework-simplejwt for the tokens. The returned JSON is formatted like:
{
access: "[JWT string]",
refresh: "[JWT string]"
}
with a token structure of:
header:
{
"typ": "JWT",
"alg": "HS256"
}
payload:
{
"token_type": "access",
"exp": 1587138279,
"jti": "274eb43bc0da429a825aa30a3fc23672",
"user_id": 1
}
When accessing the refresh endpoint, SimpleJWT requires in the data the refresh token be named refresh; for the verification and the revocation (blacklisting) endpoints the refresh token needs to be named token. Depending on what you are using for your backend will require modification from what I did.
The access token is only used in the api Authentication header and is updated when the mutations are called.
To get the token so I could decode it I used a simple shell script:
#!/usr/bin/env bash
EMAIL="my#email.com"
PASSWORD="aReallyBadPassword"
echo "API Login Token"
JSON_FMT='{"email":"%s","password":"%s"}'
JSON_FMT=` printf "$JSON_FMT" "$EMAIL" "$PASSWORD" `
curl \
--request POST \
--header Content-Type:application/json \
--data $JSON_FMT \
http://localhost:8000/api/auth/
echo ""

React Admin with OpenID Connect

I'm trying to implement a simple OpenID Connect react-admin login using gitlab as OAuth2 service provider.
most of the react-admin examples about OpenID is simple username/password login. But OpenID Connect will do several redirects, and what I come with is make python/flask server redirect to http://example.com/#/login?token=<token>, and make react-admin to parsed the URL, and set token in localStorage.
basically is somethings like below:
(({ theme, location, userLogin } ) => {
let params = queryString.parse(location.search);
if (params.token) {
userLogin({token: params.token});
}
return (
<MuiThemeProvider theme={ theme }>
<Button href={ '/api/gitlab/login' }>
Login via GitLab
</Button>
</MuiThemeProvider>
);
});
Obviously, that is not good enough, I want to have some advice about how can I improve this?
I assume you followed this example https://marmelab.com/react-admin/Authentication.html, that does not cover the OAuth2 password grant.
// in src/authProvider.js
import { AUTH_LOGIN } from 'react-admin';
export default (type, params) => {
if (type === AUTH_LOGIN) {
const { username, password } = params;
const request = new Request('https://mydomain.example.com/authenticate', {
method: 'POST',
body: JSON.stringify({ username, password }),
headers: new Headers({ 'Content-Type': 'application/json' }),
})
return fetch(request)
.then(response => {
if (response.status < 200 || response.status >= 300) {
throw new Error(response.statusText);
}
return response.json();
})
.then(({ token }) => {
localStorage.setItem('token', token);
});
}
return Promise.resolve();
}
The GitLab guys / girls describe what grants they provide.
https://docs.gitlab.com/ee/api/oauth2.html#resource-owner-password-credentials-flow
Here is the example how you can get an access token using curl:
echo 'grant_type=password&username=<your_username>&password=<your_password>' > auth.txt
curl --data "#auth.txt" --request POST https://gitlab.com/oauth/token
With the access token you can get some information from the user that is also described here: https://docs.gitlab.com/ee/api/oauth2.html#access-gitlab-api-with-access-token
Here is the example how you can get information from GitLab with the access token that you got from the previous call using curl:
curl --header "Authorization: Bearer OAUTH-TOKEN" https://gitlab.com/api/v4/user
With some small adjustments in the react-admin example you can use the password credentials flow.
Here you can find the example that works with GitLab:
https://gist.github.com/rilleralle/b28574ec1c4cfe10ec7b05809514344b
import { AUTH_LOGIN } from 'react-admin';
export default (type, params) => {
if (type === AUTH_LOGIN) {
const { username, password } = params;
const oAuthParams = {
grant_type: "password",
username,
password
}
const body = Object.keys(oAuthParams).map((key) => {
return encodeURIComponent(key) + '=' + encodeURIComponent(oAuthParams[key]);
}).join('&');
const request = new Request('https://gitlab.com/oauth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
},
body
})
return fetch(request)
.then(response => {
if (response.status < 200 || response.status >= 300) {
throw new Error(response.statusText);
}
return response.json();
})
.then(( {access_token} ) => {
localStorage.setItem('token', access_token);
});
}
return Promise.resolve();
}
I hope this helps you.
Cheers
Ralf
Here an good example using oauth flow in react-admin auth provider:
https://github.com/marmelab/ra-example-oauth/blob/master/app/src/authProvider.js

Cannot log in for some users in Auth0 SPA using VueJS

I have an access_denied error that occurs only for some users in my web app when they try to login user username/password.
Using the auth0-js 9.3.3 plugin for a VueJS 2.0 SPA using the authorization extension.
I get the following response in the logs window in Auth0. How can I debug these kind of errors and see what's the cause?
This results in the access_token and id_token being empty.
{
"date": "2018-06-03T11:15:15.478Z",
"type": "f",
"description": "Unexpected token { in JSON at position 19",
"connection": null,
"connection_id": "",
"client_id": "1ySh5N0sOXxMkcAslnuhRfxO5BloY56t",
"client_name": "IRIS",
"ip": "80.57.245.139",
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36",
"details": {
"body": {
"wa": "wsignin1.0",
"wresult": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ1c2VyX2lkIjoiNWFlODM0ZTc5MjA4YjgwNThhNGEyMDFkIiwiZW1haWwiOiJtYXJnZXJ0aG8xQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJsYXN0X3Bhc3N3b3JkX3Jlc2V0IjoiMjAxOC0wNS0xMlQxNDo1Njo0My4xNzFaIiwic2lkIjoiVVZQSThRWG9RdHRlNzg4c2c0Yy14VHZqNnFRRHdQVHMiLCJpYXQiOjE1MjgwMjQ1MTMsImV4cCI6MTUyODAyNDU3MywiYXVkIjoidXJuOmF1dGgwOjUwMXN0OlVzZXJuYW1lLVBhc3N3b3JkLUF1dGhlbnRpY2F0aW9uIiwiaXNzIjoidXJuOmF1dGgwIn0.lnK7k568DtWiUUEQEqn1PIOAQGeGQ6kg2Y6cwZUyl655ae-9dA-uM4oijD3ByDwVBL8aqFxxAAZmdPOr8pSpehTgsI7WTYrZee1sT2i80zG2IaKb-0Ue8Yx_7aGNMzhXVZHdkdY13EL5gzNeV52IVlhQLmUtDL9C4LZqApjN7wk",
"wctx": "{\"strategy\":\"auth0\",\"auth0Client\":\"eyJuYW1lIjoibG9jay5qcyIsInZlcnNpb24iOiIxMC4xOC4wIiwibGliX3ZlcnNpb24iOiI4LjcuMCJ9\",\"tenant\":\"501st\",\"connection\":\"Username-Password-Authentication\",\"client_id\":\"1ySh5N0sOXxMkcAslnuhRfxO5BloY56t\",\"response_type\":\"token id_token\",\"scope\":\"openid profile email groups permissions roles\",\"protocol\":\"oauth2\",\"redirect_uri\":\"http://localhost:8080/callback\",\"state\":\"s31LeZ-DQZLfAX10cTZ4AcoP9E7-nl-w\",\"nonce\":\"0j2CVd8Aogz2sgh8MaetsgEEq-uKo0sN\",\"sid\":\"UVPI8QXoQtte788sg4c-xTvj6qQDwPTs\",\"audience\":\"https://iris.501st.nl\",\"realm\":\"Username-Password-Authentication\",\"session_user\":\"5b13cdc121652a131b057eb4\"}"
},
"qs": {},
"connection": null,
"error": {
"message": "Unexpected token { in JSON at position 19",
"oauthError": "access_denied",
"type": "oauth-authorization"
}
},
"hostname": "XXXX.eu.auth0.com",
"user_id": "auth0|5ae834e79208b8058a4a201d",
"user_name": "XXXXX#gmail.com",
"log_id": "90020180603111515478182853610644826347557693433397116978"
}
This is the code I use for the authentication:
import decode from 'jwt-decode'
import auth0 from 'auth0-js'
import Router from 'vue-router'
const ID_TOKEN_KEY = 'id_token'
const ACCESS_TOKEN_KEY = 'access_token'
const CLIENT_ID = process.env.VUE_APP_AUTH0_CLIENT_ID
const CLIENT_DOMAIN = process.env.VUE_APP_AUTH0_CLIENT_DOMAIN
const SCOPE = 'openid profile email groups permissions roles'
const AUDIENCE = process.env.VUE_APP_AUTH0_AUDIENCE
const auth = new auth0.WebAuth({
clientID: CLIENT_ID,
domain: CLIENT_DOMAIN
})
export function login () {
auth.authorize({
responseType: 'token id_token',
redirectUri: process.env.VUE_APP_AUTH0_REDIRECT,
audience: AUDIENCE,
scope: SCOPE
})
}
export function getProfile () {
const accessToken = localStorage.getItem('access_token')
if (!accessToken) {
console.log('Access token must exist to fetch profile')
}
if (accessToken) {
return new Promise((resolve, reject) => {
auth.client.userInfo(accessToken, function (err, profileData) {
if (err) {
if (err.stack) {
console.log(err.stack)
} else {
console.log(err)
}
}
if (!profileData) {
console.log('Logging out because cannot get profile data!')
logout()
reject(Error('Cannot get profile data'))
return false
}
if (profileData && !profileData.email_verified) {
alert('Je hebt een e-mail grekegen om je e-mailadres te valideren. Zodra je e-mail is gevalideerd kan je opnieuw inloggen.')
logout()
reject(Error('Email not verified'))
} else if (profileData['https://iris.501st.nl/app_metadata'].authorization.groups.length === 0) {
alert('Je account moet nog worden goedgekeurd door de GWM voordat je toegang krijgt tot IRIS. Neem contact op met de GWM.')
logout()
reject(Error('No groups configured'))
} else if (!profileData['https://iris.501st.nl/user_metadata'].costumes) {
alert('Je hebt nog geen kostuums aan je account gekoppeld, vraag de GWM om deze voor je in te regelen.')
logout()
reject(Error('No costumes configured'))
} else if (profileData) {
resolve(profileData)
} else {
console.log('Logging out because cannot get profile data!')
logout()
reject(Error('Cannot get profile data'))
}
})
})
}
}
const router = new Router({
mode: 'history'
})
export function logout () {
clearIdToken()
clearAccessToken()
router.go('/')
}
export function requireAuth (to, from, next) {
if (!isLoggedIn()) {
next({
path: '/',
query: { redirect: to.fullPath }
})
} else {
next()
}
}
export function getIdToken () {
return localStorage.getItem(ID_TOKEN_KEY)
}
export function getAccessToken () {
return localStorage.getItem(ACCESS_TOKEN_KEY)
}
function clearIdToken () {
localStorage.removeItem(ID_TOKEN_KEY)
}
function clearAccessToken () {
localStorage.removeItem(ACCESS_TOKEN_KEY)
}
// Helper function that will allow us to extract the access_token and id_token
function getParameterByName (name) {
let match = RegExp('[#&]' + name + '=([^&]*)').exec(window.location.hash)
return match && decodeURIComponent(match[1].replace(/\+/g, ' '))
}
// Get and store access_token in local storage
export function setAccessToken () {
let accessToken = getParameterByName('access_token')
if (accessToken) {
localStorage.setItem(ACCESS_TOKEN_KEY, accessToken)
}
}
// Get and store id_token in local storage
export function setIdToken () {
let idToken = getParameterByName('id_token')
if (idToken) {
localStorage.setItem(ID_TOKEN_KEY, idToken)
}
}
export function isLoggedIn () {
const idToken = getIdToken()
return !!idToken && !isTokenExpired(idToken)
}
function getTokenExpirationDate (encodedToken) {
const token = decode(encodedToken)
if (!token.exp) { return null }
const date = new Date(0)
date.setUTCSeconds(token.exp)
return date
}
function isTokenExpired (token) {
const expirationDate = getTokenExpirationDate(token)
return expirationDate < new Date()
}