Options instead of Get Request when adding custom headers - react-admin

When I try out the below code to add token to my header. My get request is not called but the options is called. This makes it unable to populate the listview in react-admin. Any advice on how to resolved this ?
import { fetchUtils, Admin, Resource } from 'react-admin';
import simpleRestProvider from 'ra-data-simple-rest';
const httpClient = (url, options = {}) => {
if (!options.headers) {
options.headers = new Headers({ Accept: 'application/json' });
}
const token = localStorage.getItem('token');
options.headers.set('Authorization', `Bearer ${token}`);
return fetchUtils.fetchJson(url, options);
};
const dataProvider = simpleRestProvider('http://localhost:3000', httpClient);
const App = () => (
<Admin dataProvider={dataProvider} authProvider={authProvider}>
...
</Admin>
); ```

Related

Problem with Authentication using Apollo and React-native

I have this authentication issue
The registration works perfectly and the servers take the registration data: User password email and number. After this step, I have OTP verification
I got the pin code and run the verification mutation.
On the verification, I got the error :
You are not authenticated
And the all process stops because I am not verified
Here is the code for the react-native part
const VERIFY = gql
mutation($token: String!, $kind: TokenKind!) {
verify(token: $token, kind: $kind)
}
const VerificationScreen: React.FC < any > = (props) => {
const token = (props as any).route.params.token;
const [loading, setLoading] = React.useState < boolean > (false)
const [pin, setPin] = useState < string > ('')
const [veryfy] = useMutation(VERIFY)
const verifyPin = () => {
if (!pin) {
alert('Please TYPE Valid PIN')
return;
}
//veryfy
setLoading(true);
veryfy({
variables: {
token: pin,
kind: 'PHONE'
}
}).then(({
data
}) => {
setLoading(false)
console.log(data);
if (data) {
props.navigation.navigate('Intro', {
token: token
});
}
}).catch((e) => {
setLoading(false)
console.log(e);
})
}
The below code is an example showing how you can use the Apollo middle-ware [1] and context [2] to add headers(auth) at runtime or testing.
First we create a middle-ware block, then an inner context block.
In the context block we can use the line below to add external parameters, this is to configure the request
const { isAuth, Authorization } = headers;
A boolean(Auth) can be set to allow a token to be embedded in a Authorization header, or an existing Authorization header can be passed in directly, this can be usefull for testing for example.
const getClient = () => {
// create link middleware see [1]
const authMiddleware = new ApolloLink((operation, forward) => {
// code block below assumes there exists an auth token in globals
// add headers with the client context [2]
operation.setContext(({ headers = {} }) => {
// auth header using global token as a bearer token
const authHeaders = {
Authorization: global.access_token
? `Bearer ${global.access_token}`
: "",
};
// here an Authorization header can be passed in thru the context,
// which can be useful, eg for testing
const { isAuth, Authorization } = headers;
// if we have an Auth.. header we can just add that and return
if (Authorization) {
return {
headers: { ...publicHeaders, ...{ Authorization } },
};
}
const header = isAuth
? { ...publicHeaders, ...authHeaders }
: publicHeaders;
return {
headers: header,
};
});
return forward(operation);
}); // end create middleware
// create the graphql endpoint [1]
const httpLink = new HttpLink({ uri: '/graphql' });
// create client with the middleware and return the client
// code block below assumes there exists a globalCache
return new ApolloClient({
cache: globalCache,
link: concat(authMiddleware, httpLink),
});
}
use
// add/configure the appropriate headers in the context block
// for the client
client
.mutate({
mutation: <some mutation>,
variables: <some variables>,
context: {
headers: {
isAuth: false, // or true for authenticated operation
},
},
})
.then((result) => ....)
.catch((err) => {
console.log(....);
});
use in a hook
const [runMutation, { data }] =
useMutation(<gql>, {
context: {
headers: { isAuth: true },
variables: <some vars>,
onCompleted: (data) => callback(data),
onError: (error) => console.error("Error ", error),
},
});
links
1 middleware
2 context

Vue apollo - send authorization over websockets

I have a vue web app from which I'm trying to run a subscription using a hasura query.
My problem is that I cannot pass to the Websocket request an authorization token as the backend expects.
These are my current settings:
const token = localStorage.getItem("token") || null;
const options = {
httpUri: //graphql http entpoint,
wsUri: //graphql ws endpoint
};
let link = new HttpLink({
uri: options.httpUri
});
// Create the subscription websocket link if available
if (options.wsUri) {
const wsLink = new WebSocketLink(
new SubscriptionClient(options.wsUri, {
lazy: true,
reconnect: true,
connectionParams: {
headers: {
Authorization: `Bearer ${token}`
}
}
})
);
// using the ability to split links, you can send data to each link
// depending on what kind of operation is being sent
link = split(
// split based on operation type
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === "OperationDefinition" &&
definition.operation === "subscription"
);
},
wsLink,
link
);
}
const authLink = setContext((_, { headers }) => {
// return the headers to the context so httpLink can read them
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : ""
}
};
});
const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors)
graphQLErrors.map(({ message }) => {
if (message.includes("unauthorized")) {
EventBus.$emit("unauthorized");
}
});
if (networkError) console.log(`[Network error]: ${networkError}`);
});
const apolloClient = new ApolloClient({
link: ApolloLink.from([errorLink, authLink, link]),
cache: new InMemoryCache(),
connectToDevTools: true
});
const apolloProvider = new VueApollo({
defaultClient: apolloClient
});
When I try to run the subscription I get
HTTP Authentication failed; no valid credentials available
And in the ws request header I cannot see my Authorization bearer set.
A side info I need authorization for both http and ws requests
I think errorLink or authLink unexpectedly change websocket header token. You try modifying a bit:
const httpLink = from([
authLink,
// errorLink,
new HttpLink({
uri: Config.httpDataHost,
headers: {
[XHasuraClientName]: Config.hasuraClientName
}
})
]);
const wsLink = new WebSocketLink({ ... });
const link = ...
const apolloClient = new ApolloClient({
link: ApolloLink.from([errorLink, link]),
cache: new InMemoryCache(),
connectToDevTools: true
});
If it doesn't work, you can try commenting errorLink to check. Another thing is, you shouldn't get token globally, but use lazy function so ApolloClient can always get latest access token from local storage
const getIdToken = () => localStorage.getItem('token') || null;
const wsLink = new WebSocketLink({
uri: options.wsUri,
options: {
connectionParams: () => ({
headers: {
Authorization: getIdToken(),
}
}),
...
}
});
PS: I have an example repository with React + Apollo Client 3.0. Although you are using Vue.js,Apollo Client construction is the same https://github.com/hgiasac/ra-hasura-typescript-boilerplate/blob/auth-jwt/src/shared/ApolloClient.ts

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

Multi-part form data in react-admin

I'm trying to use react-admin to send data to my custom API. I want to send files, I can see that there is , I'd like to send that data as multi-part form data. I have come across the base64 encoding help page, as a newcomer to react, it is hard for me to figure out what I need to do to turn it in to multi-part form data.
If someone could walk me through the code that makes it work, that'd be great! I'm here to learn.
Thanks so much in advance.
I had the same problem, this is my solution:
import { fetchUtils } from "react-admin";
import restServerProvider from 'ra-data-json-server';
const servicesHost = 'http://my-services-host';
const httpClient = (url, options = {}) => {
if (!options.headers) {
options.headers = new Headers({ Accept: 'application/json' });
}
const token = localStorage.getItem('token');
options.headers.set('Authorization', `Bearer ${token}`);
return fetchUtils.fetchJson(url, options);
};
const dataProvider = restServerProvider(servicesHost, httpClient);
const myDataProfider = {
...dataProvider,
create: (resource, params) => {
if (resource !== 'resource-with-file' || !params.data.theFile) {
// fallback to the default implementation
return dataProvider.create(resource, params);
}
let formData = new FormData();
formData.append('paramOne', params.data.paramOne);
formData.append('paramTwo', params.data.paramTwo);
formData.append('theFile', params.data.theFile.rawFile);
return httpClient(`${servicesHost}/${resource}`, {
method: 'POST',
body: formData,
}).then(({ json }) => ({
data: { ...params.data, id: json.id },
}));
}
};
export default myDataProfider;

How to add hmac in grapqhl queries from client?

We are using apolloclient in our React Native project, can anyone guide us on how we can add hmac in queries made using it?
I hope this help
import ApolloClient, { gql } from 'apollo-boost';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { print } from 'graphql/language/printer';
const baseURL = 'http://localhost:3000/';
const cache = new InMemoryCache({
addTypename: false
});
const apolloClient = new ApolloClient({
uri: baseURL,
cache,
request: (operation) => {
const { query, variables, operationName } = operation;
const payload = {
variables,
operationName,
query: print(query)
}
console.log(payload)
// custom headers
const headers = {
'X-API-KEY': '332dace0-e629-48bf-b664-d550ebfc828c',
'X-APP-ID': '52557818-b027-43a8-bf45-fb87f98cd96e',
'content-type': 'application/xjson',
'Authorization': token ? `Bearer ${token}` : '',
}
console.log(headers)
const token = ''; // create HMAC here
operation.setContext({
headers: {
...headers,
}
})
}
});