is there a way to use Laravel sanctum token to be able to access protected routes (Laravel Project) - laravel-9

I'm building a website witch has some protected routes that I want to prevent non-authenticated users from accessing it, So I'm using Laravel Sanctum for that purpose. I was testing this using postman, but now I want to actually use it in production from backend, So how I suppose to do that token that was generated after login!
Thanks in advance.

I think the answer is late, but it may be useful to someone else
You will set your login routes
// Login Route
Route::middleware('guest')->group(function () {
Route::post('/login', LoginAction::class)->name('auth.login');
});
// Logout Route
Route::middleware('auth:sanctum')->group(function () {
Route::post('/logout', LogoutAction::class)->name('auth.logout');
});
public function __invoke(AuthRequest $request)
{
$username = $request->input('username');
$password = $request->input('password');
$remember = $request->input('remember');
// I use filter_var to know username is email or just the regular username
$field = filter_var($username, FILTER_VALIDATE_EMAIL) ? 'email' : 'username';
// User::where('username', $username)->first();
// User::where('email', $username)->first();
$user = User::where($field, $username)->first();
if (
!$user || !Hash::check($password, $user->password)
|| !Auth::attempt(
[
$field => $username,
'password' => $password
],
$remember
)
) {
// Return Error Message
return $this->error(['message' => trans('auth.failed')], Response::HTTP_UNAUTHORIZED);
}
// After Check Useranme And Password Create Token
$token = $user->createToken($request->device)->plainTextToken;
// Return Success Message With Token
return $this->success(['message' => trans('auth.login', ['user' => $user->username]), 'token' => $token, 'user' => $user]);
in vuex Actions
export const loginAction = ({ commit }, userInfo) => {
let urlParams = new URLSearchParams(window.location.search);
return new Promise((resolve, reject) => {
login(userInfo)
.then((response) => {
if (response.success) {
commit("SET_LOGIN_USER", response.payload.user);
// Set Token Mutaion
commit("SET_TOKEN", response.payload.token);
// Notify User for success logged in
Notify.create({
type: "positive",
message: `welcome ${response.payload.user.username}`,
});
// Redirect to dashboard
router.push(
urlParams.get("redirect") || { name: "dashboard" }
);
}
resolve(response);
})
.catch((error) => {
// For any error remove any auth user details and notify him
Notify.create({
type: "negative",
message: `${error.response?.data.payload}`,
});
commit("REMOVE_AUTH_DETAILS");
reject(error);
});
});
};
in vuex mutations 3 methods
// Helpers Fuctions
import { setLoginUser, setToken, removeToken, removeLoginUser } from '../../../utils/auth';
export const SET_LOGIN_USER = (state, user) => {
state.loginUser = user
setLoginUser(user)
}
export const SET_TOKEN = (state, token) => {
state.token = token
setToken(token)
}
export const REMOVE_AUTH_DETAILS = (state) => {
state.loginUser = null
state.token = null
removeLoginUser()
removeToken()
}
My Helper Functions
// I use Quasar Framework But you can use any cookie package like js-cookie
import { Cookies } from "quasar";
const TokenName = "TOKEN";
const LoginUser = "LOGIN_USER";
export function setToken(token) {
return Cookies.set(TokenName, token);
}
export function getToken() {
return Cookies.get(TokenName);
}
export function removeToken() {
return Cookies.remove(TokenName);
}
export function setLoginUser(loginUser) {
return Cookies.set(LoginUser, JSON.stringify(loginUser));
}
export function getLoginUser() {
return Cookies.get(LoginUser);
}
export function removeLoginUser() {
return Cookies.remove(LoginUser);
}
I use Vue js for frontend and axios for making requests
in axios file request.js
// It's helper function to get token from cookie
import { getToken } from "./auth";
import router from "../router";
import store from "../store";
import axios from "axios";
// Create axios instance
const service = axios.create({
baseURL: "/api/v1/",
timeout: 10000 // Request timeout
});
// Request intercepter
service.interceptors.request.use(
config => {
if (getToken()) {
config.headers["Authorization"] = "Bearer " + getToken(); // Set Token
}
return config;
},
error => {
// Do something with request error
console.log("error-axios", error); // for debug
Promise.reject(error);
}
);
// response pre-processing
service.interceptors.response.use(
response => {
return response.data;
},
error => {
// remove auth user informations from cookies and return to login page if unauthorized
if ([401].includes(error.response.status)) {
store.commit('auth/REMOVE_AUTH_DETAILS')
router.push({ name: "login" });
}
return Promise.reject(error);
}
);
export default service;
// Logout Fuction in controller or action
public function __invoke(Request $request)
{
if(! $request->user()) {
return $this->error(['message' => trans('auth.no_auth_user')]);
}
// Remove Token
$request->user()->tokens()->delete();
return $this->success([
'message' => trans('auth.logout', ['user' => auth()->user()->username])
], Response::HTTP_OK);
}
// Logout mutation (vuex)
export const logoutAction = ({ commit }) => {
return new Promise((resolve, reject) => {
logout()
.then(() => {
Notify.create({
type: "negative",
message: `see you later ${getLoginUser().username}`,
});
commit("REMOVE_AUTH_DETAILS");
router.push({ name: "login" });
resolve();
})
.catch((error) => {
router.push({ name: "login" });
commit("REMOVE_AUTH_DETAILS");
reject(error);
});
});
};
Any other help I will not be late

Related

how to update the refreshToken on page refresh in next-auth?

Summary
I have implemented next-auth to get the accessToken from the provider and pass the same as argument to my mutation, then use the token generated from our backend on consecutive API calls.
The token rotation has been implemented based on the next-auth doc refresh token rotation
Problem
After 1 min (my token expiry time) if the session is untouched/running in background then probably it calls the api and updates with the new refreshToken (works as intended) but when we refresh/reload the page after one minute it signs out of the session.
When I check the terminal, it looks like when I reload the page, the old refreshToken is not getting updated with new refreshToken. It still has the same refreshToken (the one session had before reload) in client but no the issue when we leave the app untouched 😕
Is there any work around for this? Or what am I doing wrong here?
Thanks in advance!
Snippet
here's the pages/api/[..nextauth].tsx
import NextAuth, { NextAuthOptions } from "next-auth"
import { JWT } from "next-auth/jwt"
import GithubProvider from "next-auth/providers/github"
import { Provider, RefreshToken } from "../../../generated/graphql"
const loginQuery = `mutation login($AccessToken: String!, $Provider: Provider!) {
login(accessToken: $AccessToken, provider: $Provider) {
refreshToken
token
tokenExpiry
}
}`
const refreshTokenQuery = `mutation refreshToken($RefreshToken: String!) {
refreshToken(refreshToken: $RefreshToken) {
refreshToken
token
tokenExpiry
}
}`
const authMutation = (query, variables) : Promise<RefreshToken | null> => {
return fetch(process.env.NEXT_PUBLIC_API_URL, {
method: 'POST',
body: JSON.stringify({
query: query,
variables: variables
})
})
.then(res => res.json())
.then(data => {
if (data.errors) {
return null;
} else {
let response = data.data.refreshToken || data.data.login;
if (response.refreshToken === undefined ||
response.tokenExpiry === undefined ||
response.token === undefined) return null
return <RefreshToken> response;
}
})
.catch(error => {
console.log(error);
return null;
});
};
async function login(account, user: any) {
let provider: Provider;
if (account?.provider === 'github') provider = Provider.GitHub
if(provider === undefined) return
const variables = {
AccessToken: account.access_token,
Provider: provider
};
const response = await authMutation(loginQuery, variables)
if (response === null) return
console.log("new refresh token..", response.refreshToken)
return {
accessToken: response.token,
accessTokenExpires: response.tokenExpiry * 1000,
refreshToken: response.refreshToken,
user,
}
}
async function refreshAccessToken(token: any) {
const variables = {
RefreshToken: token.refreshToken,
};
console.log("variables", variables)
const response = await authMutation(refreshTokenQuery, variables)
if (response === null) return
console.log("new refresh token..", response.refreshToken)
return {
...token,
accessToken: response.token,
accessTokenExpires: response.tokenExpiry * 1000,
refreshToken: response.refreshToken,
}
}
export const authOptions: NextAuthOptions = {
providers: [
GithubProvider({
clientId: process.env.NEXT_PUBLIC_GITHUB_OAUTH_CLIENT_ID,
clientSecret: process.env.NEXT_PUBLIC_GITHUB_OAUTH_CLIENT_SECRET,
}),
],
theme: {
colorScheme: "light",
},
callbacks: {
jwt: async ({ account, token, user }: any): Promise<JWT> => {
console.log("current refreshToken", token.refreshToken, token.accessTokenExpires)
// Initial Sign In
if (account && user) {
console.log("initial login", account)
return login(account, user)
}
// Return previous token if the access token has not expired yet
if (Date.now() < token.accessTokenExpires){
console.log("token not expired")
return token
}
// Rotate refresh_token and fetch new access_token if current one is expired
if (token){
console.log("token expired")
return refreshAccessToken(token)
}
},
session: async ({ session, token }: any) => {
if (!session?.user || !token?.accessToken) return
session.accessToken = token.accessToken as string
session.error = token.error as string | undefined
session.user = token.user
return session
},
},
}
export default NextAuth(authOptions)
and this how _app.tsx looks like,
import "../styles/globals.css"
import { SessionProvider as NextSessionProvider } from "next-auth/react"
import UrqlProvider from "../lib/provider"
import { AppProps } from "next/app"
function App({ Component, pageProps }: AppProps) {
const { session } = pageProps
return (
<>
<NextSessionProvider session={session}>
<UrqlProvider>
<Component {...pageProps} />
</UrqlProvider>
</NextSessionProvider>
</>
)
}
export default App

How to set authorization header coorectly?

Problem:
In my react native app in order to remove repeated calls I have developed a general POST GET methods in httpClient file. It code is look likes this.
import axios from 'axios';
import AsyncStorage from '#react-native-community/async-storage';
axios.defaults.headers.post['Content-Type'] = 'application/json';
var instance = null;
const setAuthorisationHeder = async () => {
const token = JSON.parse(await AsyncStorage.getItem('auth_data'));
if (token) {
console.log('>>>>>> instance', instance);
Object.assign(instance.headers, {
Authorization: 'Bearer' + token.accessToken,
});
} else {
console.log('>>>>>> instance', instance);
Object.assign(instance.headers, {
Authorization: '',
});
}
};
export const setHeader = () => {
console.log('>>>>>>>> HIIII');
instance = axios.create({
baseURL: '',
timeout: 150000,
headers: {
'Content-Type': 'application/json',
},
});
instance.interceptors.response.use(
function (response) {
return response;
},
async function (error) {
if (error.response.status) {
if (error.response.status === 401) {
AsyncStorage.removeItem('auth_data');
} else {
throw error;
}
} else {
console.log(error);
}
},
);
};
export const Get = (route, data) => {
function getData() {
return instance.get(
route,
data == null ? {data: {}} : {data: JSON.stringify(data)},
);
}
if (instance) {
console.log('>>>>>> HIIIIii');
// setAuthorisationHeder();
return getData();
}
return setHeader().then(getData);
};
export const Post = (route, data) => {
console.log('>>>>>> route', route);
function postData() {
return instance.post(route, JSON.stringify(data));
}
if (instance) {
console.log('>>>>>> HIIIIii');
// setAuthorisationHeder();
// setAuthorisationHeder();
return postData();
}
return setHeader().then(postData);
};
Can some tell me a way to add an authorization header to this instance? My token is storing the Asyncstorage in the middle of some actions so at the beginning called I don't have the token. As my code setHeader is running only one time so I created a method call setAuthorisationHeder() function. But it is giving me can not find property .then error when I am putting a request. Can someone help me to solve this issue? Thank you?
you can define global headers once and use it in every network call.
https://github.com/axios/axios#global-axios-defaults
Create a global auth variable where you'll store the auth data from storage. Before making a request get the auth data and use interceptor to set the bearer token.
let authToken = '';
const getAuthToken = async () => {
// asumming auth token was saved as string
authToken = await AsyncStorage.getItem('auth_data');
};
Interceptor
// request interceptor
axiosInstance.interceptors.request.use(
function (config) {
// Do something before request is sent
config.headers.Authorization = `Bearer ${authToken}`;
return config;
},
function (error) {
// Do something with request error
return Promise.reject(error);
}
);
complete code
import axios from 'axios';
import AsyncStorage from '#react-native-community/async-storage';
let authToken = '';
const axiosInstance = axios.create({
baseURL: '',
timeout: 150000,
headers: {
'Content-Type': 'application/json',
},
});
// request interceptor
axiosInstance.interceptors.request.use(
function (config) {
// Do something before request is sent
config.headers.Authorization = `Bearer ${authToken}`;
return config;
},
function (error) {
// Do something with request error
return Promise.reject(error);
}
);
const getAuthToken = async () => {
// asumming auth token was saved as string
authToken = await AsyncStorage.getItem('auth_data');
};
export const Get = async (route, data = {}) => {
// get and set auth token
await getAuthToken();
// route = /user?id=787878 or /user/787878
return await axiosInstance.get(route);
};
export const Post = async (route, data = {}) => {
await getAuthToken();
return await axiosInstance.post(route, data);
};

Return to request after refreshing tokens

I am trying to get refresh tokens working.
It works for the most part, but the problem is I'm not sure how to return to where the original call was before the access token needed refreshing.
It doesn't work the first time it refreshes, after that the token is refreshed and then it works ok again until it expires.
So the problem is I cant get it returning to where it started on the refresh
Here is an example from the component
created(){
axios.get("http://localhost:63861/api/sampledata/WeatherForecasts").then(response => {
console.log(response.data);
//**** DO STUFF WITH THE DATA. I WANT TO GET BACK HERE AFTER REFRESH
})
.catch(error => {
console.log(error);
});
I need to get back to the point where it can do stuff with the data once it has refreshed and reset the access tokens.
my interceptor:
import axios from "axios";
import store from "../store";
import Storage from "../services/storage";
import { REFRESH } from "../store/actions.type";
export default function execute() {
// Request
axios.interceptors.request.use(
config => {
var token = Storage.getAccessToken();
if (token) {
console.log("Bearer " + token);
config.headers["Authorization"] = "Bearer " + token;
}
return config;
},
error => {
return Promise.reject(error);
}
);
// Response
axios.interceptors.response.use(
response => {
return response;
},
error => {
console.log("Error need to refresh");
const originalRequest = error.config;
// token expired
if (error.response.status === 401) {
originalRequest._retry = true;
let tokenModel = {
accessToken: Storage.getAccessToken(),
client: "Web",
refreshToken: Storage.getRefreshToken()
};
var refreshPath = "auth/" + REFRESH;
store
.dispatch(refreshPath, { tokenModel })
.then(response => {
console.log(response);
return axios(originalRequest);
})
.catch(error => {
console.log(error);
// Logout
});
}
return Promise.reject(error);
}
);
}
You need to return your refresh promise.
return store
.dispatch(refreshPath, { tokenModel })
.then(response => {
console.log(response);
return axios(originalRequest);
})
.catch(error => {
console.log(error);
// Logout
});
What is happening now is you dispatch the action, then your return Promise.reject(error) is ran. By returning the refresh promise, you ensure axios waits for that chain to finish

NuxtJs - Cannot read property 'headers' of undefined

I'm a newbie in NuxtJs. I'm trying to implement an external API Call with axios which I get token and store it on cookie. Everything works well in development. But when I try to run npm run generate it gives me errors that I don't know what to do.
When I delete nuxtSeverInit, npm run generate runs smoothly. And after some research, i think that nuxtServerInit that I'm using shouldn't be used. Can anyone please tell me how to make it work.
This is the first project in a new company, so I'm trying to prove myself. Please help me with it. Will you.
Click here for image that shows the error that appears after npm run generate
This is store/index.js file
import Vuex from 'vuex'
var cookieparser = require('cookieparser')
const createStore = () => {
return new Vuex.Store({
state: {
auth: null,
},
mutations: {
update (state, data) {
state.auth = data
}
},
actions: {
nuxtServerInit ({ commit }, { req }) {
let accessToken = null
if (req.headers.cookie) {
var parsed = cookieparser.parse(req.headers.cookie)
if(parsed){
accessToken = parsed.auth
}
}
commit('update', accessToken)
},
}
})
}
export default createStore
middleware/authenticated.js file
export default function ({ store, redirect }) {
// If the user is not authenticated
if (!store.state.auth) {
return redirect('/login')
}
}
middleware/notAuthenticated.js file
export default function ({ store, redirect }) {
// If the user is authenticated redirect to home page
if (store.state.auth) {
return redirect('/app/dashboard')
}
}
login.vue file
validateBeforeSubmit() {
this.$validator.validateAll().then((result) => {
if (result) {
this.button_title = 'One moment ...';
let submitted_user_data = {
'username': this.emailAddress,
'client_id': this.user_uuid,
'password': this.password,
}
MerchantServices.do_user_login(submitted_user_data)
.then(response => {
let access_token = response.data.access_token;
this.postLogin(access_token);
})
.catch(error => {
this.$refs.invalid_credentials.open();
this.button_title = 'Sign in'
});
return;
}
});
},
postLogin: function(access_token_val) {
if(access_token_val != ''){
setTimeout(() => {
const auth = {
accessToken: access_token_val
}
this.$store.commit('update', auth)
Cookie.set('auth', auth)
this.$refs.invalid_credentials.open();
this.button_title = 'Sign in'
this.$router.push('/app/dashboard')
}, 1000)
}else{
alert('hello')
}
},
and the last user login api call which also returns the token.
do_user_login(user){
var user_details = 'username='+user.username+'&client_id='+ user.client_id +'&grant_type=password&password='+user.password+''
return axios.post('myapiurl', user_details )
.then(response => {
return response;
});
},
Acording to Nuxt Docs req is not available on nuxt generate.
You should use nuxt build and than nuxt start after that.

stateless session api request

I am building a simple app that uses JWT for authentication. But I keeps on getting the error saying the route I GET to require a call back function.
What do I expect?
I should be getting the current user's data back.
What do I actually get?
Error: Route.get() requires a callback function but got a [object Object]
Route:
const authenticate = require("../middlewares/authenticate");
const usersController = require("../controllers").users;
app.get("/users/me", authenticate, usersController.getMe);
Model:
"use strict";
const jwt = require("jsonwebtoken");
module.exports = (sequelize, DataTypes) => {
var User = sequelize.define(
"User",
{
email: DataTypes.STRING,
password: DataTypes.STRING
},
{
classMethods: {
associate: function(models) {
// associations can be defined here
},
findByToken: function(token) {
const User = this;
let decoded;
try {
decoded = jwt.verify(token, "leogoesger");
} catch (e) {
console.log(e);
}
return User.find({ where: { email: decoded.email } });
}
}
}
);
return User;
};
Middleware:
const { User } = require("../models/user");
const authenticate = (req, res, next) => {
console.log("called here");
const token = req.header("x-auth");
User.findByToken(token)
.then(user => {
if (!user) {
}
req.user = user;
req.token = token;
next();
})
.catch(e => {
res.status(401).send(e);
});
};
module.exports = { authenticate };
Controller:
module.exports = {
getMe(req, res) {
res.status(200).send({ message: "hello" });
}
};
Your authenticate module exports an object, yet you do this:
const authenticate = require("../middlewares/authenticate");
which means your const authenticate is an object, not your function. Change that to this:
const authenticate = require("../middlewares/authenticate").authenticate;
Or, change the module to export the function directly instead of exporting an object with the function in it.