Using tokens to authorize in Sanctum - javascript

I want to use React with Sanctum. In Sanctum, I generated the token and return it after login, and it works correctly. However, after login, I can see my token in the console, but if I want to open the route for logged-in users, I must send this token in the header. What is the best method to do that?
public function login(Request $request)
{
$attr = $request->validate([
'email' => 'required|string|email|',
'password' => 'required|string|min:6'
]);
if (!Auth::attempt($attr)) {
return $this->error('Credentials not match', 401);
}
return response()->json([
'access_token' => auth()->user()->createToken('auth_token')->plainTextToken,
'token_type' => 'Bearer',
]);
}
And login into React.
function Login () {
const emailRef = useRef(),
passwordRef = useRef();
const handleSubmit = (event) => {
event.preventDefault();
axios.post('http://localhost:8000/api/auth/login', { email: emailRef.current.value, password: passwordRef.current.value })
.then(res => {
console.log(res);
}).catch((error) => {
console.log(error)
});
}
return(
// Here is my form
);
}
#Edit
I don't sure I good understand but it doesn't work. If I try to get address witch need to autorize I have a status 401.
I make instance
instance.php
import axios from 'axios'
let instance = axios.create({
baseURL: 'http://localhost:8000',
headers: {
common: {
'X-Requested-With': 'XMLHttpRequest',
}
},
withCredentials: true,
});
export default instance;
And I login user. In React it look like this
instance.post('/api/auth/login', {
email: emailRef.current.value,
password: passwordRef.current.value
}).then(res => {
console.log(res);
})
Generally it works. I want to add someting which need to authorize.
instance.get('/api/me')
.then(res => {
console.log(res.data.data.token);
}).catch((error) => {
console.log(error)
});
And in Api.php it look like this
Route::post('/auth/register', [AuthController::class, 'register']);
Route::post('/auth/login', [AuthController::class, 'login']);
Route::group(['middleware' => ['auth:sanctum']], function () {
Route::get('/me', function() {
echo "Hello World";
})->name('me');
Route::post('/auth/logout', [AuthController::class, 'logout']);
});

You should be able to not need to store anything in any special place by just using Axios config like this:
axios.create({
headers: {
common: {
'X-Requested-With': 'XMLHttpRequest',
},
},
withCredentials: true,
});
The withCredentials: true will store the CSRF token and any credential once you login, so you would not have to worry anymore about sending any credentials or anything like so.
This is another example.
You could also take advantage of Axios and also delete any data you have store when a request returns 401 (Unauthorized) or 419 (Session Expired).
authClient.interceptors.response.use(
(response) => response,
(error) => {
if (error.response &&
[401, 419].includes(error.response.status) &&
store.getters[`user/${IS_GUEST_GETTER}`]
) {
store.dispatch(`user/${LOGOUT_ACTION}`);
}
return Promise.reject(error);
}
);
In this example, I am using a store like vuex and I am asking first if the current user is a guest or not, and then deleting everything from the session with a simple logout (on Vue and Laravel).
You can do this with Interceptors.

How to Fix CSRF token issue using JavaScript (without jQuery)
Error I was facing.
419 PAGE EXPIRED
message : "CSRF token mismatch.", exception: "Symfony\Component\HttpKernel\Exception\HttpException"
My Route is protected with auth:sanctum, above snippet also worked with auth middleware.
Route::post('/media/upload', MediaController::class)
->middleware('auth:sanctum')
->name('media.upload');
1st add <meta name="csrf-token" content="{{ csrf_token() }}"> in app.blade.php
2nd: Get the csrf-token value using document.querySelector('meta[name="csrf-token"]').getAttribute('content')
Snippet headers settings
headers: {
"Accept": "application/json",
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
},
"Accept": "application/json" for 419 PAGE EXPIRED fixing.
'X-CSRF-TOKEN': document.querySelector('me... for CSRF token mismatch.
It worked either you are using axios, Ajax, XHR, uppy.io XHRUpload, or SPA
$.ajax({
type:'POST',
url:'/ajax',
headers: {
"Accept": "application/json",
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
},
});

Related

HTTP Fetch Spotify API token request failure

so basically under guidance of the Spotify WebAPI doc I am trying to request an access token via Client Credentials method. Spotify API Doc. I want to use a regular HTTP fetch request, I can not use any 3rd party libraries. I am getting a 400 return status error response: {error: "unsupported_grant_type", error_description: "grant_type parameter is missing"}. However I believe my request should be formated correctly for its grant type. I have looked at tons of articles, MDN doc, and the Spotify doc and I can not figure out why this is not working. I will include the code which I have obviously taken the api keys out of but they are correct. Link to code.
import React, { Component, useState , useEffect } from 'react';
//Custom IMPORTS:
import '../PageCss/HeaderSection.css'
const Spotify = () => {
const [baseUrl, setBaseUrl] = useState("https://accounts.spotify.com/api/token");
const [token, setToken] = useState([]);
const [currentStatus, setStatus] = useState(false);
const client_id = '';
const client_secret = '';
const data = { grant_type: 'client_credentials' };
useEffect(() => {
fetch(baseUrl,
{
method: 'POST',
headers: {
"Content-Type": "application/x-www-form-urlencoded",
'Authorization': 'Basic ' + (client_id + ':' + client_secret).toString('base64')
},
redirect: 'follow',
body: JSON.stringify(data),
})
.then((response) => {
if (!response.ok) {
return Promise.reject(new Error("Response Error!"));
}
else {
return response.json();
}
})
.catch((err) => {
console.log(err);
})
.then((json) => {
try {
setToken(json.results);
setStatus(true);
console.log("TOKEN:" + token)
}
catch
{
return Promise.reject(new Error(`State Error!: Data: ${token} , Connection:${currentStatus}`));
}
})
.catch((err) => {
console.log(err);
})
}, [baseUrl]);
return (
<div >
</div>
)
};
export default Spotify;
My application is a react app, hosted on GitHub. It's a fully functioning site and everything else is working fine. My other API fetch calls are working fine so I know this one must have an issue in it. The only line of code giving me an error is this 400 status from the fetch request.
Hey so I actually got the inital token request to work with this code:
fetch(baseUrl,
{
method: 'POST',
body: 'grant_type=client_credentials&client_id=' + client_id + '&client_secret=' + client_secret,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
Inspired by the OAuth doc and some more researching. It works now and I have a token from Spotify.

Bearer auth token stored in localstorage passed in multiple API calls (Cypress)

I'm trying to use the bearer auth token received from a Cypress command in some tests but I'm getting an error
Cypress error
Here is my command code:
Cypress.Commands.add("getToken", () => {
cy.request({
method: 'POST',
url: 'https://login.microsoftonline.com/abcc1234-abc12345-abcc1234-abc12345/oauth2/v2.0/token',
form: true,
body: {
'client_id': 'abcc1234-abc12345-abcc1234-abc12345abcc1234-abc12345/'
'client_secret' : 'J2Kq.XXXXXXXX.Xt_-XXXXXX',
'grant_type': 'client_credentials'
}
})
.then((response) =>{
const tokenAuth = response.body.access_token;
window.localStorage.setItem('auth', tokenAuth);
And here is my test:
describe('API Testing', ()=>{
beforeEach(() => {
cy.getToken()
})
it('Connector - GET', () =>{
let a = "Bearer " + localStorage.getItem('auth')
cy.log(a);
cy.request({
method:'GET',
url:'https://XXXX-XXXXX-XXXXXXXXX.azurewebsites.net/api/v1/connectors/cost-max-and-power-intervals',
auth : 'a'
})
.then((response) =>{
expect(response).to.have.property('status', 200)
expect(response.body).to.not.be.null
expect(response.body).to.have.property('costMax', 35)
})
})
I've also tried to set a header in request but without luck. :(
Thank you in advance!
Looks like I've made a confusion, auth is not the same with Autorization. I managed to make it work by replacing auth with Autorization : a

Typescript removes Authorization header from POST and PATCH fetch requests

I've built an API using C# that uses JWT tokens for authorization. On the frontend I store these tokens in local storage and get them, when creating a request. When creating GET or DELETE requests, everything works fine, and using console.log() I can see that fetch options have the Authorization header added. However when using POST or PATCH methods, the Authorization header is missing immediatly after adding it to the object. Here is my request method:
const send = async (apiOptions: ApiParams): Promise<FetchReturn> => {
const accessToken = GetAccessToken()
const options: ApiOptions = {
method: apiOptions.method,
headers: {
Authorization: `Bearer ${accessToken}`
}
}
console.log(options)
if (apiOptions.data) {
options.headers = {
'Content-Type': 'application/json'
}
options.body = JSON.stringify(apiOptions.data)
}
const result = await fetch(`${getUrl()}/${apiOptions.path}`, options).then(res => res).catch(err => err)
if (!result.ok) {
if (IsExpired()) {
const refreshResult = await fetch(`${getUrl()}/api/user/refresh`, {method: 'POST', headers:{
'Content-Type': 'application/json'
}, body: JSON.stringify(GetRefreshRequest())}).then(res => res).catch(err => err)
if (refreshResult.ok) {
Login(JSON.parse(await refreshResult.text()))
return await send(apiOptions)
} else if (refreshResult.status === 401) {
Logout()
window.location.reload()
return { code: 0, text: ""}
}
}
}
const text = await result.text()
return { code: result.status, text: text }
}
I suppose that in apiParams for POST you have property 'data' assigned, and later you have if-condition that completely replaces request headers object.
Change it to:
options.headers['Content-Type'] = 'application/json';
To keep authorization in headers
The first time check your apiOptions.data
i think , its null when you call POST/Patch request
Just put console.log("...") In the if statement , Then try for resolve your Error
If your problem not resolved, put a replay under my post

Login page won't redirect to route to logged-in page

I've been struggling now with my login page to make the component render the Loggedin component.
I'm having Reactjs for the frontend and a NodeJS backend.
I'm fairly new to nodejs, express and react.
On the loginform component I do a post using fetch which passes the username and password to corresponding endpoint on the backend. No problem.
At the backend it reads the jsonfile where i store users(not using any DB for this project) to find a match and if both username and password match the response it send is true. And i've tested that this works.
LoginForm Frontend:
handleSubmit= (e) => {
e.preventDefault()
console.log(this.state)
const { username, password} = this.state;
const data = {username: username, password:password}
fetch('http://localhost:3001/Login', {
method: 'POST',
mode:'cors',
body: JSON.stringify(data),
headers: {
"Content-type": "application/json"
}
})
.then(function(res){
return res.json()}).then(function(resjson){
console.log(resjson);
if (resjson){
console.log(resjson);
return<Redirect to='/myAccount'/>
}
if(resjson==false){
console.log(resjson);
}
})
}
I've been trying to make use of react-router-dom by experimenting. But no matter how i went with it the component for the logged in users never renders evens if resjson is true, not even the route changes to "localhost:3000/myAccount".
I've also tried adding useHistory but that results in an invalid Hook when i add const history=useHistory();
Any ideas? If you need me to add anything else i'll do it, since i'm not that experience when it comes to JS i might have left something important out.
Thanks in advance!
Take a look below, i wrote explanations in comments :
fetch("http://localhost:3001/Login", {
method: "POST",
mode: "cors",
body: JSON.stringify(data),
headers: {
"Content-type": "application/json",
},
})
.then(function (res) {
return res.json();
})
.then(function (resjson) {
console.log(resjson);
if (resjson) {
console.log(resjson);
// here you're returning a React Component and it could work inside render !
// return <Redirect to="/myAccount" />;
// you could use history to redirect:
// if you're inside a class component you could use withRouter HOC instead (there is a link below)
return history.push("/myAccount");
}
if (resjson == false) {
console.log(resjson);
}
});
here is the link for the withRouter HOC use.

How to login user to spring-security app using Vue.js?

Hi it's my first post on stack overflow. I want to separate back-end from front-end and create REST API using my Spring Boot app. I use spring-security to login. It use something called JSESSION_ID but how to login and authorize users using Vue.js? I want to make form for login to my service and authorize request with JSESSION_ID.
It's my first app in Vue.js. I try to use code from tutorials and docs but it didn't solve my problem. I also have issues with CORS.
export default {
name: 'Login',
data(){
return{
username: '',
password: '',
error: false
}
},
updated(){
if (localStorage.token){
this.$router.replace(this.$route.query.redirect || '/')
}
},
methods:{
login(){
this.$http.post('http://localhost:8080/login', { username: this.username, password: this.password })
.then(request => this.loginSuccessful(request))
.catch(() => this.loginFailed())
},
loginSuccessful (req) {
if (!req.data.token) {
this.loginFailed();
return
}
this.error = false;
localStorage.token = req.data.token;
this.$router.replace(this.$route.query.redirect || '/')
},
loginFailed () {
this.error = 'Login failed!';
delete localStorage.token
}
}
}
I expect to login and authorize users from Vue.js using REST Spring-Boot API.
The problem is that you try to pass the object { username: this.username, password: this.password } as the data to the request, but you do not specify the Content-Type, which probably defaults to 'text/html' or 'application/json'. You could try to first convert that into form data and then pass it to the request, here is an example with AXIOS:
login() {
let formData = new FormData();
formData.set("username", this.username);
formData.set("password", this.password);
AXIOS.post('/login', formData, {headers: {'Content-Type': 'application/x-www-form-urlencoded'}})
.then((result) => {
this.loginSuccessful(result);
})
.catch((error) => {
this.loginFailed();
})
}
Perhaps it would also work without specifying the header, like AXIOS.post('/login', formData, null), haven't tried it.

Categories

Resources