I am trying to use firebase authentication to get a google access token in order to call the youtube API.
I am able to initially get the access token like this:
const provider = firebase.auth.GoogleAuthProvider();
cosnt scopes = []; // ...scopes
scopes.forEach(scope => provider.addScope(scope));
firebase.auth().signInWithPopUp(provider)
.then(userCredentials=> {
const oauthCredentials = userCredentials.credentials;
// using credentials for API calls
axios.get("some-google-api-path", { params: { access_token: oauthCredentials.accessToken } }); // and so on...
});
This works fine until the access token expires.
As far as I can tell, it seems like firebase automatically refreshes the session, but I can find a way to get the new access token.
I tried:
firebase.auth().onAuthStateChange(user => {
// could not find the access token on the user object
});
And since that failed, I tried to do it manually using:
const token = firebase.auth.GoogleAuthProvider.credential(
oauthCredentials.idToken,
oauthCredentials.accessToken
);
const authResult = await firebase.auth().signInWithCredential(token);
The issue is that authResult will only contain the idToken or the accessToken, but not both, depends on what I give the firebase.auth.GoogleAuthProvider.credential function.
Am I missing something?
Is there another/better way to do it?
There are two tokens in play here:
The refresh token, which is only available after the user actively signs in. Since it doesn't change, you can cache this token - which is what the Firebase SDKs actually also do.
The ID token, which is available at any moment from the current user. This token expires in an hour, and is auto-refreshed by the Firebase SDKs.
So if you want to retain access to the refresh token, you will have to store (typically in local storage) yourself when the user first actively signs in.
So apparently firebase doesn't refresh tokens of other providers for you (not event google) according to this (thank you Frank van Puffelen!)
So what I did instead, is authenticate manually to google (since I use react, I used react-google-login), and got all tokens from there.
Then, once the session is initiated/refreshed, I create a firebase session using:
const token = firebase.auth.GoogleAuthProvider.credential(
oauthCredentials.idToken,
oauthCredentials.accessToken
);
const authResult = await firebase.auth().signInWithCredential(token);
I hope that this helps anyone, and I will accept another answer if firebase ever changes this.
Related
Good day all,
I have a little issue in my react code and I hope someone here would be able to help me fix it.
Basically I am fetching data from an endpoint (multiple)
Am using axios and react query.
In my get request, I have security headers and one is Authorization.
User is authorized by a token sent to session storage when he / she login.
I get the token from session storage and place it in my authorization header.
My problem now is before the user login, the token === null
and my get request runs before the user logins leaving it with an empty token.
When the user finally logins, no data is fetched because what is in the authorization header is null but when I refresh the page, the data is fetched because at that time the token was already delivered and not = null.
I don't know if you fully understand cause am pretty bad at explaining but if you do, is there anyway to fix the issue.
thanks...
If you're using RTK, you may use skip:
const [skip, setSkip] = useState(true);
const { isUninitialized, isSuccess, isLoading } = useSomeQuery(token, {
skip
});
and set skip to false when the token is available
since the token is a dependency to your fetch, it should go to the queryKey. Then you can disable the query via the enabled option for as long as the token is null.
const token = useTokenFromLocalStorage()
useQuery(
["user", token],
() => fetchUser(token),
{
enabled: !!token
}
)
if the token updates (changes from null to a value), and that re-renders your component, the query will run automatically because the query key changes. The query will not run if the token is null.
I have a backend where the user gets a JSON Web Token if he is logged in successfully. This token is needed for other API calls and I want to store it application-wide to access it from every point in the application. What is the best point to store it and access it from anywhwere?
You can store it on local storage on login like
export async function login(username, password) {
const { data: jwt } = await http.post(apiEndpoint, { username, password });
localStorage.setItem("token", jwt.token);
}
and than you can access it every time like
export function getCurrentUser() {
try {
const jwt = localStorage.getItem("token");
return jwtDecode(jwt);
} catch (error) {
return null;
}
}
I have used widely this approach and works like charm.
I'd highly suggest not to store in localStorage. I'd highly recommend cookies.
See this link.
In addition, also See another link.
Storing JWT token in localstorage or session storage of a browser is not preferable, as it can be accessed easily by anyone who has even a little knowledge of browser developer options (especially developers like us).
I suggest you use http only cookie to store them, that way it can be accessed whenever you send a HTTP request.
You can read about it more - https://blog.logrocket.com/jwt-authentication-best-practices/
I am building a react native application and am using Firebase, more specifically firestore, in order to manage my data. My current objective is to implement an auto login feature on my app, where if the user exits the app, I want them to stay signed in, unless they manually hit the Sign Out button before exiting the app. Here is my current process of doing this:
When the user logs into the app, I sign them in by:
firebase.auth().signInWithEmailAndPassword(email, password).
I then get their idToken by:
let authIdToken = "";
firebase
.auth()
.currentUser.getIdToken(true)
.then(function (idToken) {
authIdToken = idToken
})
.catch(function (error) {
console.log(error)
});
I then want to save this token into the phone, so when the user opens the app again, I can fetch this token and check its validity. If it is valid, then I can log the user in using their idToken. In react native, I can do this by doing:
AsyncStorage.setItem(
"userData",
JSON.stringify({
token: token,
})
);
Now when the app loads up:
const startScreen = props => {
useEffect(() => {
const tryLogin = async () => {
const userData = await AsyncStorage.getItem("userData");
const transformedData = JSON.parse(userData);
const { token } = transformedData;
await firebase
.auth()
.verifyIdToken(token, true)
.then((payload) => {
console.log(true)
})
.catch((error) => {
if (error.code == "auth/id-token-revoked") {
// Token has been revoked. Inform the user to reauthenticate or signOut() the user.
console.log("revoked")
} else {
console.log("error")
}
});
};
tryLogin();
}, []);
The Issue: When I try to verify the token this way, I am met with the following error: firebase.auth().verifyIdToken is not a function.
I read through the documentation and am unsure of how else to verify this token using JS. How do I verify it? Let me know if my verification process is incorrect and how it should be done. I am new to using firestore and doing authentication in general and hope to learn how to do it the right way.
Another helpful note: This is how I am configuring my firestore: !firebase.apps.length ? firebase.initializeApp(firebaseConfig) : {};
Thanks!
I then want to save this token into the phone, so when the user opens the app again, I can fetch this token and check its validity.
This is completely unnecessary. Firebase Auth with persist the signed in user, and automatically refresh the token without you having to do anything. All you need to do is listen to when updates to the token are made available, and act on the new token as needed. You can establish an ID token listener using onIdTokenChanged as shown in the linked API documentation:
firebase.auth().onIdTokenChanged(function(user) {
if (user) {
// User is signed in or token was refreshed.
}
});
Once you have this token, you know that the user is successfully signed in. There is nothing left to do. There is no need to use it to sign in.
Also, you can't verify the token on the frontend. The verifyIdToken method you're looking at is for the Admin SDK only, which only runs on the backend. The idea is that you get the token on the fronend, then pass it to the backend as described in the documentation for the Admin SDK. The backend uses this to securely determine if the user on the frontend is who they say they are.
Since you didn't say if you have a backend or not, dealing with this token might not be necessary at all. If you just want to know when the user is signed in (even if they are just returning to the page after being away, then you can skip everything above and just use an auth state observer. Again, Firebase Auth persists information about the user so you don't have to sign them in again. The observer will tell you when the automatic sign-in is complete, or if they are not signed in at all.
I am setting up new cypress tests to test some functionalities in Dynamics 365 application. But, I'm left with a browser window with the url https://login.microsoftonline.com/__/ and the text Whoops, there is no test to run.
describe('Initial Cypress Tests', () => {
it('navigate to D365', () => {
cy.visit('https://wipropoc.crm8.dynamics.com')
})
})
Would suggest you to directly do a POST call for getting SSO authentication token and fire cy.visit('https://wipropoc.crm8.dynamics.com') with the obtained token.
Here are the steps to follow from official documentation,
Login when authentication is done on a 3rd party server.
Parse tokens using cy.request().
Manually set tokens on local storage.
Map external hosts and point to local servers.
cy.request('POST', 'https://sso.corp.com/auth', { username: 'foo', password: 'bar' })
.then((response) => {
// pull out the location redirect
const loc = response.headers['Location']
// parse out the token from the url (assuming its in there)
const token = parseOutMyToken(loc)
// do something with the token that your web application expects
// likely the same behavior as what your SSO does under the hood
// assuming it handles query string tokens like this
cy.visit('http://localhost:8080?token=' + token)
// if you don't need to work with the token you can sometimes
// just visit the location header directly
cy.visit(loc)
})
You can read more about this here - https://docs.cypress.io/guides/guides/web-security.html#Form-Submission-Redirects
Real time example - https://xebia.com/blog/how-to-use-azure-ad-single-sign-on-with-cypress/
When successfully logged in into the cognito user pool, I can retrieve access token and id token from the callback function as
onSuccess: function (result) {
var accesstoken = result.getAccessToken().getJwtToken()
var idToken = result.idToken.jwtToken
}
But how can I retrieve the refresh token? And how can I get a new token using this refresh token. I didnot find any clear answers.
You can use result.getRefreshToken().getToken() for that.
The success callback takes CognitoUserSession object i.e. result as a parameter which exposes getRefreshToken method to retrieve refresh token.
Refer this link for Cognito JavaScript SDK documentation -
https://github.com/aws/aws-amplify/tree/master/packages/amazon-cognito-identity-js
Not sure if I clearly understand your second question, but Use case 32 in above link might help you in dealing with it.