For the following code:
const apolloClient = new ApolloClient({
cache: new InMemoryCache(),
link: new HttpLink({
uri: endpoint,
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
}
})
});
I need to get endpoint and token asynchronously. How may I do this?
Thank you
You can use apollo-link-context to modify your requests. You can cache the values as shown if you don't fetch them on every request.
let token
let uri
const contextLink = setContext(async () => {
if (!token) {
token = await getTokenAsync()
}
if (!uri) {
uri = await getUriAsync()
}
return { uri, token }
});
const client = new ApolloClient({
...
link: ApolloLink.from([
contextLink,
httpLink,
])
})
The above is the preferred way of dynamically setting these parameters. Alternatively, you could just fetch the token and URI before rendering your ApolloProvider and then dynamically create your client instance based on the values.
Related
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
I am learning GraphQL and Next.js through a Udemy Course and the author uses ApolloClient from apollo-boost package. But #apollo/client package is the new one and I started using that instead.
using apollo-boost's ApolloClient the author sets the credentials for each request like below:
new ApolloClient({
request: operation => {
operation.setContext({
fetchOptions: {
credentials: 'include'
},
headers
})
},
uri: process.env.BASE_URL,
cache: new InMemoryCache().restore(initialState || {})
})
This is not working for me as request is not present in #apollo/client's ApolloClient. So I tried like below:
const link = new HttpLink({ uri: "http://localhost:3000/graphql", credentials: 'include' });
new ApolloClient({
cache: new InMemoryCache().restore(initialState || { }),
link,
})
But the credentials are not working for each request. I am not getting user information.
I am using passport which is storing logged in user info in cookies.
Below is the index.js file for configuring passport:
const config = require('../config/dev');
const sessison = require('express-session');
const passport = require('passport');
exports.init = (server, db) => {
require('./passport').init(passport);
const sess = {
name: 'portfolio-session',
secret: config.SESSION_SECRET,
cookie: { maxAge: 2 * 60 * 60 * 100},
resave: false,
saveUninitialized: false,
store: db.initSessionStore()
}
server.use(sessison(sess));
server.use(passport.initialize());
server.use(passport.session());
}
Can somebody help with code to add authentication to ApolloClient?
Thanks :)
From the looks of it you are not actually passing the header bearing the token in your example.
Take the headers incoming from the first author's example:
[...]
fetchOptions: {
credentials: 'include'
},
headers // <- this one
})
},
[...]
Add it to the request like you were trying to do:
const link = new HttpLink({
uri: "http://localhost:3000/graphql",
headers // <-- pass it here, should contain something along these lines: { Authorization: `Bearer ${token}` } or w/e
});
const client = new ApolloClient({
cache: new InMemoryCache().restore(initialState || { }),
link,
});
I have a question about auth0 and next js.
For example, I have the next code (this code works)
//initialprops enables server-side rendering in a page and allows you to do initial data population
ModelsList.getInitialProps = async (ctx) => {
//this is static token to test from auth0.com
const accessToken = 'eyJhbG.....'
//fetching data
const res = await fetch('http://localhost:7071/api/bo/getModels', {
headers: {
Authorization: `Bearer ${accessToken}`
}
})
const json = await res.json()
return { data: json }
}
As you can see, I have accessToken variable as a text. It's a problem for me
How can make accessToken dynamic?
Thanks a lot!
P.S please, dont reference to auth0 documentation, I have tried a lot. Provide, please, a real solution/example.
Ok, so this is what worked for me.
Let's say you've got api.example.com/resources. This where data actually is. You will need to proxy via next's api.
Inside your jsx component, you fetch next's api.
// components/Dashboard.jsx
const API_URL = "api/resources";
async function fetcher(url: any) {
const res = await fetch(url);
const json = await res.json();
return json;
}
function Dashboard() {
const { data, error } = useSWR(API_URL, fetcher);
if (error) return <div>failed to load</div>;
if (!data) return <div>loading...</div>;
return <div>show your resources here</div>;
}
and now inside the next's api file you can fetch the actual endpoint you need.
// api/resources.js
import {
getAccessToken,
getSession,
withApiAuthRequired,
} from "#auth0/nextjs-auth0";
export default withApiAuthRequired(async function healthcheck(req, res) {
const session = await getSession(req, res);
const token = session?.idToken;
const response = await fetch("https://api.example.com/resources", {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
});
const data = await response.json();
res.status(200).json(data);
});
if you get errors, check the jwts you're getting. Audience or scope mismatch errors are usually the main culprits.
I'm struggling to figure out why the response I get from my API isn't mapping to an object that I have in typescript.
Here's the function in my service that calls the API:
register(user: IUser): Observable<IUser> {
var headers = new Headers();
headers.append('Content-Type', 'application/json');
var options = new RequestOptions({
headers: headers,
url: this._registerUrl,
body: JSON.stringify(user)
});
return this._http.post(this._registerUrl, { user }, options)
.map((res: Response) => res.json() as IUser)
.catch(this.handleError);
}
This is the function that calls the service:
register(): void {
let user: IUser = {
email: this.email,
username: this.username,
password: this.password
}
this._userService.register(user)
.subscribe(result => {
debugger;
if(result.errorCode > 0)
this.handleError(result.errorCode);
else {
localStorage.setItem('userId', result.userId.toString());
localStorage.setItem('username', result.username.toString());
localStorage.setItem('email', result.email.toString());
}
});
}
The object that I am returning from the API matches the object that I have in the frontend. It is returning the data and I can see it in the body of my response. All of the data is right, but it's in the body and is not turning it into an IUser object.
Does anybody have any ideas? Thanks.
EDIT
This is what the response object looks like when it comes back from the service.
I am working with authentication using Angular and .Net Web API 2 back end. My registration route, and other resources are working, however the login/token is not.
In postman, this request works and I get the token back:
In angular my code looks like the following:
credentials.grant_type = "password";
credentials.userName = "email#email.com";
credentials.password = "asdfasdf";
let headers = new Headers({ 'Content-Type': 'application/x-www-form-urlenconded' });
let options = new RequestOptions({ headers: headers });
return this.http.post('http://localhost:58352/Token', credentials, options).map((response: Response) => {
return response.json();
});
However, I get the response:
{"error":"unsupported_grant_type"}
In Angular.js (or Angular 1) I used transformRequest to get it working.
This did the trick!
let urlSearchParams = new URLSearchParams();
urlSearchParams.append('userName', 'email#email.com');
urlSearchParams.append('password', 'asdfasdf');
urlSearchParams.append('grant_type', 'password');
let body = urlSearchParams.toString()
let headers = new Headers({ 'Content-Type': 'application/x-www-form-urlenconded' });
let options = new RequestOptions({ headers: headers });
return this.http.post('http://localhost:58352/Token', body, options).map((response: Response) => {
return response.json();
});