axios firebase refresh token authorization header not override - firebase

I'm trying to refresh token(firebase) in react native without success. It add the new token but in this way
Authorization Bearer old_token, Bearer new_token
Expected behaviour
Authorization Bearer new_token
Here is my code, we can see the instance, interceptor for append current token to all request and finally the interceptor for the refresh token. I don't know what I'm missing.
const customConfig: AxiosRequestConfig = {
baseURL: 'http://localhost:3000',
headers: {
'content-type': 'application/json',
},
responseType: 'json',
};
const instance: any = axios.create(
customConfig,
);
// interceptor to put token to all request
instance.interceptors.request.use(
async (config: any) => {
const token = await AsyncStorage.getItem('token');
if (token) {
config.headers.authorization = 'Bearer ' + token;
console.log("config.headers.authorization", config.headers.authorization)
}
return config;
},
(error: any) => {
Promise.reject(error);
},
);
// interceptor to handle refresh token
instance.interceptors.response.use((response: any) => {
return response;
},
function (error: any) {
console.log("error en axios", error)
const originalRequest = error.config;
if (!error.response) {
return Promise.reject('Network Error');
}
if ((error.response.status === 401) && !originalRequest._retry) {
originalRequest._retry = true;
return firebase.auth().currentUser?.getIdTokenResult(false).then((res) => {
AppStorage.setToken(res.token).then(() => { console.log('Token saved'); });
const addToken = 'Bearer ' + res.token;
instance.defaults.headers.common['Authorization'] = addToken;
originalRequest.headers['Authorization'] = addToken;
return axios(originalRequest);
});
}
return Promise.reject(error);
},
);

Related

use state to build url query string

I am new to Redux, so any help would be appreciated.
I want to add a variable to my fetch GET request URL inside the action creator.
yourapi.com/getuser/{user1}
I might not be following the correct process, I am very new to redux. I am using NextJS with React-Redux for this project.
My action:
// Get User Object
export const load_user = () => async dispatch => {
try {
const res = await fetch(`/api/getuser`, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}
});
const data = await res.json();
if (res.status === 200) {
dispatch({
type: LOAD_USER_SUCCESS,
payload: data
});
} else {
dispatch({
type: LOAD_USER_FAIL
});
}
} catch(err) {
dispatch({
type: LOAD_USER_FAIL
});
}
};
That part seems ok.
In this getuser.js file, the action calls (The action creator) how do I append a username variable onto the URL ${API_URL}/GetUser/{username} ?
export default async (req, res) => {
if (req.method === 'GET') {
const username = ??????????
try {
// How to get username???
const apiRes = await fetch(`${API_URL}/GetUser/username`, {
method: 'GET',
headers: {
'Accept': 'application/json',
}
});
const data = await apiRes.json();
if (apiRes.status === 200) {
return res.status(200).json({
user: data
});
} else {
return res.status(apiRes.status).json({
error: data.error
});
}
} catch(err) {
return res.status(500).json({
error: 'Something went wrong when retrieving user'
});
}
} else {
// Error. Not a GET request. They tried POST or PUT etc.
res.setHeader('Allow', ['GET']);
return res.status(405).json({
error: `Method ${req.method} not allowed`
});
}
};
I tried
const user = useSelector(state => state.user)
but I get the error
Invalid hook call error - TypeError: Cannot read properties of null (reading 'useContext')

Status code 200, but empty response. What is problem?

I have a problem. When I send a login request to the server, I get a status code of 200, but no token in response. Can you tell me what the problem is
import { createApi, fetchBaseQuery } from "#reduxjs/toolkit/query/react";
import { setCredentials, logOut } from "../services/features/authSlice";
const baseQuery = fetchBaseQuery({
baseUrl: "https://central-park.doniraj-krv.w3lab.cloud",
mode: "no-cors",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
prepareHeaders: (headers, { getState }) => {
const token = getState().auth.token;
console.log(token);
if (token) {
headers.set("Authorization", `Bearer ${token}`);
}
// headers.set("Accept", "application/json");
// headers.set("Content-Type", "application/json");
return headers;
},
});
const baseQueryWithReauth = async (args, api, extraOptions) => {
let result = await baseQuery(args, api, extraOptions);
if (result?.error?.originalStatus === 403) {
console.log("sending refresh token");
// send refresh token to get new access token
const refreshResult = await baseQuery(
"/api/auth/refresh",
api,
extraOptions
);
console.log(refreshResult);
if (refreshResult?.data) {
const user = api.getState().auth.user;
// store the new token
api.dispatch(setCredentials({ ...refreshResult.data, user }));
// retry the original query with new access token
result = await baseQuery(args, api, extraOptions);
} else {
api.dispatch(logOut());
}
}
return result;
};
export const apiSlice = createApi({
reducerPath: "api",
baseQuery: baseQueryWithReauth,
endpoints: (builder) => ({}),
});
When I send POST request, I have status code 200 but response is empty

Is it possible to add more scopes to NextAuth provider during session?

I am currently using NextAuth to signIn in my application, and want to add more scopes into it while the user is already signed in so I can use the Google Fit API.
I've been reading the documentation of NextAuth and doing some research but did not find anything helpful for the current NextAuth v4 in this scope situation.
My current Google configuration:
import NextAuth from 'next-auth';
import GoogleProvider from "next-auth/providers/google"
const GOOGLE_AUTHORIZATION_URL =
'https://accounts.google.com/o/oauth2/v2/auth?' +
new URLSearchParams({
prompt: 'consent',
access_type: 'offline',
response_type: 'code'
})
export default NextAuth({
// Configure one or more authentication providers
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
authorization: GOOGLE_AUTHORIZATION_URL,
}),
],
callbacks: {
async jwt({ token, user, account }) {
// Initial sign in
if (account && user) {
return {
accessToken: account.access_token,
accessTokenExpires: Date.now() + account.expires_in * 1000,
refreshToken: account.refresh_token,
user
}
}
// Return previous token if the access token has not expired yet
if (Date.now() < token.accessTokenExpires) {
return token
}
// Access token has expired, try to update it
return refreshAccessToken(token)
},
async session({ session, token }) {
session.user = token.user;
session.accessToken = token.accessToken
session.error = token.error
return session
}
},
jwt: {
secret: process.env.NEXTAUTH_JWT_SECRET,
},
secret: process.env.NEXTAUTH_SECRET,
})
async function refreshAccessToken(token) {
try {
const url =
"https://oauth2.googleapis.com/token?" +
new URLSearchParams({
client_id: process.env.GOOGLE_CLIENT_ID,
client_secret: process.env.GOOGLE_CLIENT_SECRET,
grant_type: "refresh_token",
refresh_token: token.refreshToken,
})
const response = await fetch(url, {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
method: "POST",
})
const refreshedTokens = await response.json()
if (!response.ok) {
throw refreshedTokens
}
return {
...token,
accessToken: refreshedTokens.access_token,
accessTokenExpires: Date.now() + refreshedTokens.expires_at * 1000,
refreshToken: refreshedTokens.refresh_token ?? token.refreshToken, // Fall back to old refresh token
}
} catch (error) {
console.log(error)
return {
...token,
error: "RefreshAccessTokenError",
}
}
}
My current code is working just fine, so I just need the scopes to authorize and use the Google Fitness API.
Actually made it work, created a file called add_scopes.js inside pages/api/auth/
export default (req, res) => {
if (req.method === 'POST') {
// construct the authorize URL with additional scopes
const scopes = 'openid https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/fitness.activity.read https://www.googleapis.com/auth/fitness.location.read'
const redirectUri = process.env.GOOGLE_CALLBACK_URL
const clientId = process.env.GOOGLE_CLIENT_ID
const authorizationUrl = `https://accounts.google.com/o/oauth2/v2/auth?prompt=consent&access_type=offline&response_type=code&scope=${scopes}&redirect_uri=${redirectUri}&client_id=${clientId}`
// send the authorization URL to the client
res.json({ authorizationUrl });
} else {
res.status(405).end(); // Method Not Allowed
}
}
then made a button to call this api route:
import { useCallback } from 'react';
import { Button } from 'react-bootstrap';
const AddScopesButton = ({scopes=scopes}) => {
const isAuthorized = scopes.includes("https://www.googleapis.com/auth/fitness.activity.read") && scopes.includes("https://www.googleapis.com/auth/fitness.location.read")
const handleClick = useCallback(async () => {
try {
const res = await fetch("/api/auth/add_scopes", { method: "POST" });
const json = await res.json()
if (res.ok) {
window.location.href = json.authorizationUrl;
} else {
throw new Error(res.statusText);
}
} catch (error) {
console.error(error);
}
}, []);
return (
<>
{!isAuthorized && (
<Button className='mt-2' onClick={handleClick}>Add Scopes</Button>
)}
{isAuthorized && <span>Authorized</span>}
</>
);
};
export default AddScopesButton;
The only problem is if you signOut and signIn back in you need to get the authorization again, would really like to know if there is a way to save the accessToken/scopes that were authorized.

Axios getStore is undefined in NextJs api calls. (Redux, NextJs, Jwt)

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!

How to get logged user in vuejs

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
}
});

Resources