I am trying to implement authentication in Graphql using firebase and websockets (on react native).
The client uses the firebase to authenticate and gets a token. It then sends the token to the server over a websocket client, which validates the user using the admin sdk.
I am facing two problems:
When the app boots up, it establishes a ws connection which by that time, it has no authorization header. The user gets a token after a while using firebase.
The token expires after some time, so after a while I need to update the authorization header in the websocket connection, and re-run the query, mutation or subscription which got rejected because of the expired token.
Is there a way to update the authorization header and re-run the query?
Do I need to close the previous connection and open a new one using the new token in the authorization header? How is this done?
I am using apollo-server, apollo-client, apollo-link, subscriptions-transport-ws.
I haven't run into your exact issue before, but you should check out connectionParams field. If on startup a new websocket client is created, you can fetch a new token asynchronously in the connectionParams.
import { createClient } from 'graphql-ws';
export const createWebsocketClient = (user) => createClient({
url: 'ws://localhost:8080/v1/graphql',
connectionParams: async () => {
const token = await user.getToken();
return {
headers: {
Authorization: `Bearer ${token}`,
},
};
},
});
The token is only sent when initializing the connection, so even if the token expires after the initializing, it shouldn't be a problem.
Related
I want to identify currently signed-in user on my nodejs server. To do so securely, after a successful sign-in, I have to send the user's ID token to your server using HTTPS.
As in firebase docs
firebase.auth().currentUser.getIdToken(/* forceRefresh */ true).then(function(idToken) {
// Send token to your backend via HTTPS
// ...
}).catch(function(error) {
// Handle error
});
If the token is sent to the backend server using AJAX post request then what should be the URL in xhr request var xhr = new XMLHttpRequest(); xhr.open('POST', url , true); and how to recieve it on nodejs backend server app.js file.
Or there is any other method to do it?
You can add an authorization header in request and parse the header value in your nodejs app.
xhr.setRequestHeader('Authorization', firebaseTokenId);
In your nodejs application you can do:
function abc(req, res) {
authHeader = req.get('authorization');
}
I will often have an expired authorization token in my app.
I do not, however, want this error to block requests that do not require authorization. What is the work around?
I'd like to customize my headers for requests to simply view a page (which doesn't require token, so send with an empty header) and for requests to edit data (add token and allow error to block request).
An invalid token, with headers set like below for every request, is now blocking the simple fetching of open data:
const client = new ApolloClient({
uri: "http://localhost:8000/graphql",
request: operation => {
const token = sessionStorage.getItem('jwtToken');
operation.setContext({
headers: {
'x-token': token || '',
},
});
},
});
I'm making a client-side request out to V2 of the Square API using Vue and Axios. My Vue component is as follows:
import axios from 'axios';
export default {
mounted() {
var instance = axios.create({
baseURL: 'https://connect.squareup.com/v2/',
timeout: 1000,
headers: {
'Authorization': 'Bearer xxxxxxxxxxxxxxxxx',
'Accepts': 'application/json',
'Content-Type': 'application/json'
}
});
instance.get('catalog/list')
.then(function (response) {
console.log(response);
}) ;
}
}
However, when I make that call, I receive the following error:
Failed to load https://connect.squareup.com/v2/catalog/list: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://local-env.dev' is therefore not allowed access. The response had HTTP status code 403.
That error suggests that there is some configuration that has to happen on the Square side, but I saw no opportunity to whitelist domains, etc.
Has anyone come across this error before, regardless of service, and if so, how did you resolve?
I don't think the Square API supports being called from a browser. I used Postman to do an OPTIONS request on https://connect.squareup.com/v2/catalog/list and the response was a NOT_FOUND. The OPTIONS request is needed for proper CORS support.
Plus, if you did this, I would think your auth token would need to be sent to the client -- thus exposing it to everyone. It looks like the Square API is only designed to be called from a server. But that is just based on me skimming the docs a bit. I have no experience using their API.
When doing OAuth authorization request you are not supposed to do it from your application. Create and URL with the parameters and open it in a new browser window or tab, Something like:
const grants='MERCHANT_PROFILE_READ CUSTOMERS_READ CUSTOMERS_WRITE PAYMENTS_READ PAYMENTS_WRITE PAYMENTS_WRITE_ADDITIONAL_RECIPIENTS PAYMENTS_WRITE_IN_PERSON';
const params = new HttpParams()
.set('scope', grants)
.set('client_id', <YourSquareApplicationId>)
.set('state', '1878789');
const requestUrl = `${<squareUrl>}/oauth2/authorize?${params.toString()}`;
window.open(requestUrl, "_blank");
That new window is supposed to ask the end user to login to his account and accept or deny the request.
I use Larave+JWT and vue2 + vuex2 + axios
So when user logins I store auth token in vuex store. When the token expires I need to refresh it. In order to refresh it I need to send the same token to /refresh route, and get a new token. At least that's how I got it and actually it works.
The problem is that interceptor catches 401 responses and tries to refresh token, but what if, say, in my component I send many requests with expired token? Since ajax requests are async, the interceptor code runs many times. So I got many refresh requests. Once the initial token is refreshed it is not considered valid. When interceptor tries to refresh invalid token server responds with error and I redirect to login page.
Here is the code:
axios.interceptors.response.use((response) => {
return response;
}, (error) => {
const originalRequest = error.config;
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true
axios.post('auth/refresh').then((response) => {
let token = response.data.token
store.dispatch('auth/setAuthToken', token)
let authorizationHeader = `Bearer ${token}`
axios.defaults.headers = { 'Authorization': authorizationHeader }
originalRequest.headers['Authorization'] = authorizationHeader
return axios(originalRequest)
}, (error) => {
store.dispatch('auth/clearAuthInfo')
router.push({ name: 'login' })
})
}
return Promise.reject(error);
});
I think you'll have to change your approach on how you refresh your token. Leaders like Auth0 recommends proactive periodic refresh to solve this problem.
Here is a SO answer where they talk about it.
Set the token expiration to one week and refresh the token every time the user open the web application and every one hour. If a user doesn't open the application for more than a week, they will have to login again and this is acceptable web application UX.
I have a Django app that authenticates using Okta:
headers = {
'Authorization': 'SSWS {}'.format(<okta api token>),
'Accept': 'application/json',
'Content-Type': 'application/json'
}
authentication_payload = {
'username': <username>,
'password': <password>
}
response = requests.post(
<okta auth endpoint>,
headers=headers,
data=json.dumps(authentication_payload)
)
This works successfully. From the response content I am able to get the User Id:
content = json.loads(r.content.decode('utf-8'))
okta_user_id = content['_embedded']['user']['id']
I then use the okta_user_id to create the endpoint to get the okta user by id:
okta_user_endpoint = https://<org>.okta.com/api/v1/users/<okta_user_id>
I then use the same headers from the authentication call, with the same api token, and try to get the user by id:
user_response = requests.get(
okta_user_endpoint,
headers=headers
)
But this is unsuccessful. I get a 401 error with the following content:
{
"errorCode":"E0000011",
"errorSummary":"Invalid token provided",
"errorLink":"E0000011",
"errorCauses":[]
}
Seems straight forward with an invalid token, but if the token is invalid how am I able to successfully make the authentication call? And if the token if valid for the authentication call why is it not working to get the user by id?
Okta recently changed the way that the /authn endpoint works. The /authn endpoint no longer requires an authentication token. This was done in order to support single-page applications.
It looks like your application will need to be able to fetch user information on an arbitrary user. In that case, using an Okta API token makes sense.
However, if you were making that call from a single-page application, you would want to make a request to the /users/me API endpoint.