I'm using the JWT Authentication for WP-API plugin to generate the token.
User crendetials are submitted to /jwt-auth/v1/token endpoint then store token to cookie then setState as follow
setAuth({ status: 'SIGNED_IN', user: response.data.user_display_name});
then on restricted page I check if user is valid from getServerSideProps
export const authenticateUser = async (ctx) => {
try {
const response = await axios.get(`${process.env.NEXT_PUBLIC_API_URL}/wp/v2/users/me`, {}, {
headers: {
'Authorization': `Bearer ${token}`
}
});
// if is it returns the right informations
return { status: 'SIGNED_IN', user: response.data.user}
// else
return { status: 'SIGNED_OUT', user: null}
}
}
export const getServerSideProps = async context => {
// retrieve user
const auth = await authenticateUser(context);
console.log(auth);
// if there is no authenticated user, redirect to homepage
if (auth.status === 'SIGNED_OUT') {
return {
props: {},
redirect: { destination: "/" },
}
}
return { props: { user: auth.user } }
}
In postman the requests are working fine using the right authorization bearer token.
But in my nextjs app I have the following response
{
code: 'rest_not_logged_in',
message: 'Vous n’êtes actuellement pas connecté.',
data: { status: 401 }
}
I also tried to check if user id exists using
const response = await axios.get(`${process.env.NEXT_PUBLIC_API_URL}/wp/v2/users/${userId}`, {}, {
headers: {
'Authorization': `Bearer ${token}`
}
});
but I have the following response
{
code: 'rest_user_cannot_view',
message: 'Désolé, vous n’avez pas l’autorisation de lister les comptes.',
data: { status: 401 }
}
Any help will be appreciate.
EDIT
It appears that the problem come from axios, fetch API works great.
Related
I am trying to set up authentication for a project. Once a user signs up for our app they get sent to our home page with an id in the query. This id then gets used to submit user and then the jwt token gets saved inside redux state.
All our calls now go through an axios client where the jwt token is passed on every request. The token gets read with store.getState(injectStore)
This all works fine inside getserversideProps, but the issue comes in when using calls on the frontend that goes through NextJs built in 'pages/api' folder. Any calls inside those folders causes the store.getState() to be undefined. I do not understand why since it uses the exact same client as geserversideProps.
Example GetServersideProps(working)
try {
const response = await serverApiClient.get('v1/config');
return {
props: {
},
};
} catch ({ error: { statusCode = 500, message = 'Internal Server Error' } }) {
if (statusCode === 401) {
return {
redirect: {
permanent: false,
destination: '/',
},
};
}
throw new Error(message as string);
}
};
Example Frontend bff call(not working)
try {
// Call below get sent to next built in api
const players = await apiClient.get(`/defenders?sortBy=${statId}&team_id=${teamShortName}`);
return players;
} catch (error) {
return { error };
}
};
export default async function handler(req: NextApiRequest) {
console.log('Start request')
try {
const { sortBy, team_id: teamId } = req.query;
const response = await serverApiClient.get(`/v1/players/picks?position=DEF&sort_by=${sortBy}&team_id=${teamId}`);
Api Client
mergeConfigs(
params: Record<string, string>,
headers: Record<string, string>,
configs: Record<string, string>,
): AxiosRequestConfig {
const defaultConfigs = ApiClient.getDefaultConfigs();
*const token = store?.getState()?.jwtToken?.value*
//ISSUE ABOVE - This store .getState() is only undefined in nextJS api folder calls.
return {
...defaultConfigs,
...configs,
params,
headers: {
...defaultConfigs.headers,
...headers,
...(token ? { Authorization: `Bearer ${token}` } : {}),
},
};
}
get(
uri: string,
params = {},
headers = {},
configs = {},
): Promise<AxiosResponse | any> {
return this.client
.get(uri, this.mergeConfigs(params, headers, configs))
.then((response) => {
return (response.data ? response.data : response);
})
.catch((error) => {
const errorObject = {
error: error?.response?.data,
};
throw Object.assign(errorObject);
});
}
If anyone has some advice on why that getStore is undefined in frontend-to-backend calls please assist. Thanks all!
I use next-auth.js with Google as my login provider and Django as my backend. To protect pages in next.js, I am trying to integrate next-auth.js with next.js middleware. Reference link
The issue I have is when the user is logged out, the middleware correctly routes to the login page. But after successful login, the user is redirected to the login page again. What am I missing?
middleware.js
export { default } from "next-auth/middleware"
export const config = { matcher: ["/jobs/:path*", "/accounts/:path*", "/profile/:path*", "/uploads/:path*"] }
/pages/api/auth/[...nextauth.js]
import axios from "axios";
import NextAuth from "next-auth"
import Google from "next-auth/providers/google";
import { isJwtExpired } from "../../../constants/Utils";
async function refreshAccessToken(token) {
try {
const response = await axios.post(
process.env.NEXT_PUBLIC_BACKEND_BASE + "/api/auth/token/refresh/", {
refresh: token.refreshToken
});
const { access, refresh } = response.data;
return {
...token,
accessToken: access,
refreshToken: refresh,
}
} catch (error) {
console.log(error)
return {
...token,
error: "RefreshTokenError"
}
}
}
export default NextAuth({
providers: [
Google({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
authorization: {
params: {
access_type: "offline",
response_type: "code",
scope:'openid profile email'
}
}
}),
],
callbacks: {
async jwt({ token, user, account}) {
// Initial sign in
if (account && user) {
if (account.provider === "google") {
const { access_token: accessToken } = account;
try {
// make a GET request to the DRF backend
const response = await axios.get(
process.env.NEXT_PUBLIC_BACKEND_BASE + "/api/auth/register-by-token/google-oauth2/",
{
params:
{
access_token: accessToken
}
}
);
const { access, refresh } = response.data;
token = {
...token,
accessToken: access,
refreshToken: refresh,
};
return token
} catch (error) {
console.log(error)
return {
...token,
error: "NewUserTokenError"
}
}
}
return {
...token,
error: "InvalidProviderError"
}
}
if (isJwtExpired(token.accessToken)) {
return refreshAccessToken(token)
} else {
return token
}
},
async session({ session, token }) {
session.accessToken = token.accessToken
session.refreshToken = token.refreshToken
session.error = token.error
return session
}
}
})
Upgrading next-auth.js to 4.7.0 with next.js at 12.2.0 fixed it for me.
I've run into the same problem and I was able to get it working correctly by disabled prefetching on the <Link prefetch={false} href={'/protected-route'}/> component associated with the protected pages in the application. I think that the prefetched version is cached and, upon successful singIn(), the cached version is served.
I hope it helps!
In nextjs 11.1.4 and NextAuth 4.18.8 this problem still persist
i fixed issue like this.
import { withAuth } from "next-auth/middleware"
// i used advanced middleware configuration
export default withAuth(
function middleware(req) {
// some actions here
},
{
callbacks: {
authorized: ({ token }) => {
// verify token and return a boolean
return true
},
},
}
)
export const config = { matcher: ["/jobs/:path*", "/accounts/:path*", "/profile/:path*", "/uploads/:path*"] }
I made a jwt authetication using asp.net core and vuejs
this is my auth controller :
[Route("Login")]
[HttpPost]
public IActionResult Login(LoginArgument loginArgument)
{
var user = _userService.GetByEmail(loginArgument.Email);
if (user == null) return BadRequest(error: new { message = "Invalid credential : verify email" });
if (!BC.BCrypt.Verify(text: loginArgument.Password, hash: user.Password))
{
return BadRequest(error: new { message = "Invalid credential : verify password" });
}
var jwt= _jwtService.Generate(user.Id);
Response.Cookies.Append(key: "jwt", value: jwt, new Microsoft.AspNetCore.Http.CookieOptions
{
HttpOnly=false,
SameSite=Microsoft.AspNetCore.Http.SameSiteMode.None
}) ;
return Ok(user);
}
[Route("User")]
[HttpGet]
public IActionResult User()
{
try
{
var jwt = Request.Cookies["jwt"];
var token = _jwtService.Verify(jwt);
int userId = int.Parse(token.Issuer);
var user = _userService.GetById(userId);
return Ok(user);
}
catch (Exception)
{
return Unauthorized();
}
}
and this is the login in vue
<script lang="ts">
import { reactive } from 'vue';
import { useRouter } from "vue-router";
export default {
name: "Login",
setup() {
const data = reactive({
email: '',
password: ''
});
const router = useRouter();
const submit = async () => {
await fetch('https://localhost:44391/api/Auth/Login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify(data)
});
await router.push('/Countries');
}
return {
data,
submit
}
},
}
the login part is working in front and back sides perfectly and i can see the cookies
the problem is when i try to get the logged user. in back side i can get it successfully but in front it says that no user is logged
here is the loggedUser vue
<script lang="ts">
import { onMounted, ref } from 'vue';
export default {
name: "LoggedUser",
setup() {
const message = ref('You are not logged in!');
onMounted(async () => {
const response = await fetch('https://localhost:44391/api/Auth/User', {
headers: { 'Content-Type': 'application/json' },
credentials: 'include'
});
const content = await response.json();
message.value = `hi ${content.name}`;
});
return {
message
}
}
}
Here is the errors i got when i inspect the page :
this issues appear the moment of login
1- Mark cross-site cookies as Secure to allow setting them in cross-site contexts
2- Migrate entirely to HTTPS to have cookies sent to same-site subresources
this one appears when i call loggedUser in front even so it works in the back side
{type: "https://tools.ietf.org/html/rfc7235#section-3.1", title: "Unauthorized", status:
401,…}
status: 401
title: "Unauthorized"
traceId: "00-b4a9f6fee8dff6439952ded0bb50005d-43c9aee84c454b40-00"
type: "https://tools.ietf.org/html/rfc7235#section-3.1"
You need to send the access token in the request headers
Example:
let token = '???'
const response = await post('localhost/api/auth/user', {
headers: {
'Content-Type': 'application/json'
'Authorization' : 'Bearer '+ token
}
});
I am trying to access an endpoint with username and password but the console give me a 401()
this is my code:
created () {
this.$http.get(URL, {
username: 'xxxxxxx',
password: 'xxxxx'
}).then(response => {
console.log(response)
})
}
Is it the correct way to access an endpoint with VueJS?
You have to provide the username and password codified in Base64.
const encodedUserPswd = btoa(`${user}:${pswd}`);
axios.get(URL, {}, {
headers: { `Authorization: Basic ${encodedUserPswd}` },
}).then((response) => {
// ...
});
I'm authenticating users iva JWT Auth on WP Rest API from a React Native app, so, something like that:
export function userAuth(username, password) {
return dispatch => {
dispatch(requestLogin());
const appAuth = new FormData();
appAuth.append('username', username);
appAuth.append('password', password);
return fetch(wp.jwtEndpoint, {
method: 'POST',
body: appAuth
})
.then(function(res) {
return res.json();
})
.then(function(body) {
if(body.token){
getUserDataFromUsername(body.user_nicename, body.token, dispatch);
return dispatch(userSuccessLogin(body));
}
else {
return dispatch(userFailLogin(body));
}
});
}
}
The response for this request is:
{ token: 'eyJ0eXAiOiJKXXXQ',
user_email: 'supertest#gmail.com',
user_nicename: 'super-test-avatar',
user_display_name: 'TEST TEST' }
My issue is: as I can't get user ID from there, how can I retrieve user data form a request like https://www.wp.com/wp-json/wp/v2/users/${userId}?
I tried using https://github.com/dest81/wp-api-get-user-by-username, which would allow me to do that based on username, but its endpoints goes to 404, so I think it's outdated.