How to get idToken from Expo GoogleSignIn API (expo v32)? - react-native

I am trying to setup native Google authentication on react-native using Expo GoogleSignIn API, but can't get idToken for authorized Google User.
It has accessToken in response, but not idToken.
So I am using some straigtforward code like
const result = await GoogleSignIn.initAsync({
isOfflineEnabled: true,
isPromptEnabled: true,
//webClientId: 'webClientId',
clientId // now testing only on Android, so this is irrelevant
});
console.log(result);
Example response:
Object {
"auth": Object {
"accessToken": "accessToken",
"accessTokenExpirationDate": null,
"idToken": null, // here is the problem!
"refreshToken": null,
},
"displayName": "Danila Tsvetkov",
"email": "email#email",
"firstName": "Danila",
"hostedDomain": undefined,
"lastName": "Tsvetkov",
"serverAuthCode": null,
"uid": "uid",
}
At the same time Google OAuth API returns not only accessToken, but also idToken and refreshToken.
Everything else works fine, like authorization and sign in flow.
Maybe the problem is with serverAuthCode?
Tried to put webClientId, api stops working properly. Added SHA1 to google-services (more info), didn't help. Changing other params like "isOfflineEnabled" also doesn't do much.

Can you use this
const result = await Google.logInAsync({
androidClientId: "android id"
iosClientId: 'Ios Id',
scopes: ['profile', 'email'],
});
then
if (result.type === 'success') {
console.log(result.idToken);
//or
Alert.alert(result.idToken);
}

if (result.type === 'success') {
console.log(result.user.auth);
}
this is because we are using google-sign-in, instead of expo-google-app-auth
so in conclusion, use result.user.auth, but before that, your code should look something like this
signInAsync = async () => {
try {
await GoogleSignIn.askForPlayServicesAsync();
const result = await GoogleSignIn.signInAsync();
if (result.type === 'success') {
this.onSignIn(result.user.auth);
return result.user.auth;
}
} catch ({ message }) {
alert('login: Error:' + message);
}
};

Related

The OAuth response flow failed

I'm using aws amplify along with react-native-inappbrowser. I enabled Google SSO authentication and it is working fine, but when I added GitHub auth it doesn't work as expected.
I added a listener using Hub.listen('auth', async (data) => {...} and the urlOpener in my awsConfig looks like this:
onst urlOpener = async (url, redirectUrl, settings) => {
try {
if (await InAppBrowser.isAvailable()) {
const { type, url: newUrl } = await InAppBrowser.openAuth(
url,
redirectUrl,
{ ...inAppBrowserSettings, ...settings },
);
if (type === 'success') {
Linking.openURL(newUrl);
}
} else {
Linking.openURL(url);
}
} catch (error) {
Alert.alert(error.message);
}
};
The Hub.listener looks like this:
useEffect(() => {
const unsubscribe = Hub.listen('auth', async (data) => {
const { payload } = data;
const user = payload.data;
switch (payload.event) {
case AuthEnum.SIGN_IN:
handleSignIn(user);
break;
case AuthEnum.SIGN_OUT:
handleSignOut();
break;
case AuthEnum.SIGN_IN_FAILURE:
handleSignInFailure();
break;
}
});
return () => unsubscribe();
}, []);
When I try to authenticate using GitHub, the GitHub API returns the correct token but the aws Hub catches a SIGN_IN_FAILURE and the data looks like this:
{
"channel": "auth",
"payload": {
"event": "signIn_failure",
"data": {},
"message": "The OAuth response flow failed"
},
"source": "Auth",
"patternInfo": []
}
I'm not sure why this is happening. A solution could be to remove the listener in case of GitHub auth, but I'm not sure how to do that since the InAppBrowser.openAuth() is used for both the Google SSO and GitHub auth.
Make sure to let me know if the info I provided isn't enough. Any suggestions/help would be great!
I don't think AWS Amplify support github oauth. I believe the supported ones are: Google, Apple, Facebook & Amazon
Supported social logins

FCM not working in Firebase Cloud Function because admin sdk authentication-error

I am currently trying to send FCM using Firebase Cloud Function in the Flutter WEB environment. But it doesn't work very well
My Problem
My index.js Code
/* eslint-disable max-len */
const admin = require("firebase-admin");
const {applicationDefault} = require("firebase-admin/app");
const functions = require("firebase-functions");
const cors = require("cors")({origin: true});
admin.initializeApp({credential: applicationDefault(), databaseURL: "https://<PROJECT-ID>.firebaseio.com"});
const messaging = admin.messaging();
exports.sendNoti = functions.runWith({
allowInvalidAppCheckToken: true}).https.onRequest(async (req, res) => {
cors(req, res, async () =>{
if (res.app == undefined) {
console.log("App Check failed-precondition / The function must be called from an App Check verified app.");
}
console.log(res.app);
try {
await messaging.sendToDevice(req.body.data.targetDevices, {
notification: {
title: req.body.data.messageTitle,
body: req.body.data.messageBody,
},
});
res.set("Access-Control-Allow-Origin", "*");
res.status(200).send({"status": "success", "data": "GOOD"});
return true;
} catch (ex) {
console.log(ex);
res.set("Access-Control-Allow-Origin", "*");
res.status(200).send({"status": "fail", "data": ex});
return ex;
}
});
});
Error Message
{errorInfo: {code: messaging/authentication-error, message: An error occurred when trying to authenticate to the FCM servers. Make sure the credential used to authenticate this SDK has the proper permissions. See https://firebase.google.com/docs/admin/setup for setup instructions. Raw server response: "HTML CODE
". Status code: 401.}, codePrefix: messaging}
My Try
credential: admin.credential.applicationDefault(),
credential: admin.credential.cert("service.json"),
Use OnCall function
Use AppCheck

How to get user email using Google Sign In expo Auth Session?

At moment im using this snippet of code to sign in to google, but i cant get user email… anyone know how to do this?
var LoginGoogle = () => {
const [request, response, promptAsync] = Google.useAuthRequest({
androidClientId: 'xxxxxxx.apps.googleusercontent.com',
expoClientId: 'xxxxxxx.apps.googleusercontent.com'
},{
scopes: ["email"]
},{});
React.useEffect(() => {
if (response?.type === 'success') {
const { authentication } = response;
console.log(response);
}
}, [response]);
return (
<GoogleSocialButton disabled={!request} onPress={() => {promptAsync()}} />
)
}
response returns object with links instead of email
I wish this is written in the expo docs. I would like to add a few points from the first answer:
First if you need code snippets on how to fetch user data after getting the access token, you can refer to this github issue: https://github.com/expo/expo/issues/8384
access token can be received by the following code after receiving the response object:
const { authentication: { accessToken } } = response;
then, you can create a function like this:
async function fetchUserInfo(token) {
const response = await fetch('https://www.googleapis.com/oauth2/v3/userinfo', {
method: 'GET',
headers: {
Accept: 'application/json',
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json'
},
});
return await response.json();
}
and get the user object (which contains the user email, profile, photo, etc) by something like this inside an async function:
const user = await fetchUserInfo(accessToken);
But NOTE for the user object, using https://www.googleapis.com/oauth2/v2/userinfo and https://www.googleapis.com/oauth2/v3/userinfo, will yield slightly different result/object ; in particular for v3, since Google implements the OpenID Connect API, there is no "id" attribute anymore, "id" will be called "sub".
sources:
How to identify a Google OAuth2 user?
https://developers.google.com/assistant/identity/google-sign-in-oauth
https://github.com/expo/expo/issues/8384
Example of a user object in v3:
Object {
"email": "xxxxx#gmail.com",
"email_verified": true,
"family_name": "John Deer",
"given_name": "John",
"hd": "gmail.com",
"locale": "en",
"name": "John Deer",
"picture": "https://lh3.googleusercontent.com/a/asdfjasdklfjaslkf",
"sub": "10998837733652322",
}
Hope this helps someone in the future...!
EDIT: if you need the id_token checkout this one:
expo-auth-session/providers/google Google.useAuthRequest
I am using AuthSession as well in my RN app and I stumbled with this problem. After going through Google API Docs, found out you can pass the access token from the useAuthRequest response to https://www.googleapis.com/oauth2/v3/userinfo?access_token= ACCESS_TOKEN.

expo-auth-session/providers/google Google.useAuthRequest

I have a problem with the implementation of Google Auth within a React Native app managed with Expo.
When I try to login, the response does not contain an IdToken or the information of the user and I don't understand why...
Here is my code :
import * as Google from 'expo-auth-session/providers/google';
const [request1, response1, promptAsync1] = Google.useAuthRequest({
expoClientId: 'my-expo-id',
iosClientId: 'my-ios-id',
});
React.useEffect(() => {
if (response1?.type === 'success') {
const { authentication } = response1;
}
}, [response]);
console.log('reponse', response1)
return (
<View>
<TouchableOpacity onPress={() => promptAsync1()}>
<Text style={styles.connexionText}>Connect with Google</Text>
</TouchableOpacity>
</View>
)
And here is the response :
reponse Object {
"authentication": TokenResponse {
"accessToken": "ya29.a0AfH6SMAEdPx5RcQP57rtdr4gV8GxFD1VSLAjovOce5X1yP-a2S6inLcSHF3KlxqmbKt0Cl6Catuyuua9Jz0rtV5psUUqWWX_QT32rXJwt9LTQywQaOzyy4cwspbOLm6W063w27f7NRiizC9RBg69-Yh09OhN",
"expiresIn": "3599",
** "idToken": undefined, **
"issuedAt": 1617701320,
** "refreshToken": undefined, **
"scope": "email profile https://www.googleapis.com/auth/userinfo.profile openid https://www.googleapis.com/auth/userinfo.email",
"state": "RwgjNwizdQ",
"tokenType": "Bearer",
},
"error": null,
"errorCode": null,
"params": Object {
"access_token": "ya29.a0AfH6SMAEdPx5RcQP57rtdr4gV8GxFD1VSLAjovOce5X1yP-a2S6inLcSHF3KlxqmbKt0Cl6Catuyuua9Jz0rtV5psUUqWWX_QT32rXJwt9LTQywQaOzyy4cwspbOLm6W063w27f7NRiizC9RBg69-Yh09OhN",
"authuser": "0",
"exp://172.20.10.3:19000/--/expo-auth-session": "",
"expires_in": "3599",
"prompt": "none",
"scope": "email profile https://www.googleapis.com/auth/userinfo.profile openid https://www.googleapis.com/auth/userinfo.email",
"state": "RwgjNwizdQ",
"token_type": "Bearer",
},
"type": "success",
"url": "exp://172.20.10.3:19000/--/expo-auth-session#state=RwgjNwizdQ&access_token=ya29.a0AfH6SMAEdPx5RcQP57rtdr4gV8GxFD1VSLAjovOce5X1yP-a2S6inLcSHF3KlxqmbKt0Cl6Catuyuua9Jz0rtV5psUUqWWX
_QT32rXJwt9LTQywQaOzyy4cwspbOLm6W063w27f7NRiizC9RBg69-Yh09OhN&token_type=Bearer&expires_in=3599&scope=email%20profile%20https://www.googleapis.com/auth/userinfo.profile%20openid%20https:/
/www.googleapis.com/auth/userinfo.email&authuser=0&prompt=none",
}
Thanks a lot in advance for your help !
So it's possible to get the idToken if you that's all you are looking for. You need to modify your code like this:
const [request, response, promptAsync] = Google.useAuthRequest({
*responseType: "id_token",*
expoClientId: 'my-expo-id',
iosClientId: 'my-ios-id',
});
You will also have to access the "params" key rather than "authentication," which will show mostly null :). For me it works at least since the rest of the information was useless. HTH!
Edit: I realized that I need to get an Access Token to use google drive in my app, and thus now I need both tokens and submitted a bug report here https://github.com/expo/expo/issues/12808 to try to get this resolved.
You can get user details like this:
First, get the access token from the response:
const accessToken = response.authentication.accessToken
Send GET request to the following endpoint with the accessToken you obtained in step 1:
axios.get('https://www.googleapis.com/oauth2/v3/userinfo?
access_token='+ACCESS-TOKEN-HERE)
.then(function(response){
const userDetails = response.data
console.log(userDetails)
})
Just want to add here that the id_token (Google-issued id_token) mentioned in howard's answer is a JWT that you can be decoded at jwt.io or using a jwt-decode library like jwt-decode (https://github.com/auth0/jwt-decode) or doing something like this:
import { Buffer } from "buffer";
// get response object
const id_token = response.params.id_token;
// need to decode the id_token(jwt) to get the user object, jwt issued by Google
const jwt_parts = id_token
.split(".")
.map((jwt_part) =>
Buffer.from(
jwt_part.replace(/-/g, "+").replace(/_/g, "/"),
"base64"
).toString()
);
const user = JSON.parse(jwt_parts[1]); // get user object after jwt decoded
console.log(user);
user will then have these attributes:
interface GoogleUserFromJWTPayload {
iss: string;
azp: string;
aud: string;
sub: string;
hd: string;
email: string;
email_verified: boolean;
nonce: string;
name: string;
picture: string;
given_name: string;
family_name: string;
locale: string;
iat: number;
exp: number;
jti: string;
}
But if you need both id_token and accessToken in one go, as for now it still remain as an issue/limitation?:
https://github.com/expo/expo/issues/12808

cannot access protected route with passport-jwt

I'm not able to authenticate the secret resource, I'm calling with login token still I'm getting unauthorized in response every time.
passport.use(
new JwtStrategy(
{
jwtFromRequest: ExtractJwt.fromHeader("Authorization"),
secretOrKey: jwtSecret
},
async (payload, done) => {
try {
const user = await User.findById(payload.sub);
console.log(payload.sub);
if (!user) {
return done(null, false);
}
done(null, user);
} catch (error) {
done(error, false);
}
}
)
);
controllers/users.js :
const signToken = user => {
return jwt.sign(
{
iss: "nikname",
iat: new Date().getTime(),
sub: user._id,
exp: new Date().setTime(new Date().getDate() + 1)
},
jwtSecret
);
};
route :
router.route('/secret')
.get(passport.authenticate('jwt',{session: false}),usersController.secret);
I'm not able to figure out the problem since the error is unclear.
any help ?
Thanks a lot
after using the jwt debugger , it appeared that there was an issue with payload , although the debugger showed a verified token sign, but the 'exp' and 'iat' showed wrong dates, so I changed the signToken constant like this :
const signToken = user => {
return jwt.sign(
{
iss: "nikname",
sub: user.id,
},
jwtSecret,
{
expiresIn: '2d'
}
);
};
also after researching, it appeard that fromHeader (the extractor) is not well functionning in passport-jwt#4.0.0 so I used fromAuthHeaderWithScheme instead . Like this :
jwtFromRequest: ExtractJwt.fromAuthHeaderWithScheme('JWT'),
and now it is working just fine .