I have a react native app that renders a WebView of a Web app
The react native app uses Cognito and Amplify for authentication.
The web app also uses the same Cognito and Amplify for authentication.
I have a login flow built with in the react native that has email/password login and social media federated Oauth logins. Both these login flows successfully work in the react native space and return a
CognitoUserSession {
idToken: CognitoIdToken,
refreshToken: CognitoRefreshToken,
accessToken: CognitoAccessToken,
clockDrift: 0
}
When the react native app renders the WebView the web app is unauthenticated. I am able to pass the CognitoUserSession data into the WebView successfully. Unfortunately, I don't see a way to have Amplify re-authenticate with this session.
this is the mobileLogin function I wrote that works
import Amplify, { Auth } from 'aws-amplify';
import {
CognitoUser,
CognitoUserSession,
CognitoIdToken,
CognitoRefreshToken,
CognitoAccessToken,
} from 'amazon-cognito-identity-js';
window.mobileLogin = async function(mobileSession) {
amplify = Amplify.configure({
...config().amplify,
userPoolWebClientId: '', //switch to mobile client
});
const localSession = new CognitoUserSession({
IdToken: new CognitoIdToken({ IdToken: mobileSession.idToken.jwtToken }),
RefreshToken: new CognitoRefreshToken({ RefreshToken: mobileSession.refreshToken }),
AccessToken: new CognitoAccessToken({ AccessToken: mobileSession.accessToken.jwtToken }),
});
const localUser = new CognitoUser({
Username: mobileSession.accessToken.payload.username,
Pool: Auth.userPool,
Storage: Auth.userPool.storage,
});
localUser.setSignInUserSession(localSession);
// this seems like a hack
Auth.currentCredentials = async () => localSession;
try {
await Auth.currentSession();
console.warn(`mobile login current session!!`);
store.dispatch(silentReloginAction())
} catch (ex) {
console.warn(`mobile login ${ex}`);
}
};
}
For someone who still need this.
First, you need add oauth setting to your Web application's AwsExports.json.
const AwsExports = {
Auth: {
...
oauth: {
domain: 'xxx.auth.us-east-1.amazoncognito.com',
scope:['openid'],
redirectSignIn: 'https://example.com',
redirectSignOut: 'https://example.com',
responseType: 'token'
}
},
};
then you can pass token with uri.
const session = await Auth.currentSession(),
id_token = session.getIdToken().getJwtToken(),
access_token = session.getAccessToken().getJwtToken(),
uri = `https://example.com##id_token=${id_token}&access_token=${access_token}`;
You should actually setup oauth things.
Because webview is opened as part of Oauth flow, oauth loggin out flow can be executed.
so without proper setting of oauth, error comes up
Related
I am creating one app using react native expo, which allow end user to login by their google account , and then applicaton try to save the access_token so that server based applicatin can use this to send the email on their behalf ,
But when using google sing in , i am not getting refresh token and not able to send the email ,
Here is code example which i am using
I tried below method to get the access request
const [request, response, promptAsync] = Google.useIdTokenAuthRequest({
clientId: "XXXXXXX",
androidClientId:"XXXXXXX",
iosClientId:"XXXXXXX"
});
const [initializing, setInitializing] = useState(true);
const [user, setUser] = useState();
const sendNotification=useNotification()
//console.log(sendNotification)
useEffect(() => {
if (response?.type === "success") {
const { id_token } = response.params;
const auth = getAuth();
const credential = GoogleAuthProvider.credential(id_token);
signInWithCredential(auth, credential);
let decoded = jwt_decode(id_token);
socialLogin(decoded)
}
}, [response]);
And on server using this code to sending email
const { google } = require('googleapis');
const path = require('path');
const fs = require('fs');
const credentials = require('./credentials.json');
// Replace with the code you received from Google
const code = 'XXXXXXX';
//const code="XXXXXXX"
const { client_secret, client_id, redirect_uris } = credentials.installed;
const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]);
oAuth2Client.getToken(code).then(({ tokens }) => {
console.log('first')
const tokenPath = path.join(__dirname, 'token.json');
fs.writeFileSync(tokenPath, JSON.stringify(tokens));
console.log('Access token and refresh token stored to token.json');
}).catch(err=>console.log(err));
async function signInWithGoogleAsync() {
try {
const result = await Google.logInAsync({
androidClientId: YOUR_CLIENT_ID_HERE,
scopes: ["profile", "email"],
});
if (result.type === "success") {
onSignIn(result);
return result.accessToken;
} else {
return { cancelled: true };
}
} catch (e) {
return { error: true };
}
}
Well, I tried to create an application with Google login. To use the Google Sign-In method in a React Native Expo app, you will need to perform the following steps:
Set up a project in the Google Cloud Console and obtain a configuration file for your app.
Install the expo-google-sign-in package in your React Native app.
Import the GoogleSignIn object from the expo-google-sign-in package and use the initAsync method to initialize the Google Sign-In process.
Use the GoogleSignIn.askForPlayServicesAsync method to check if the device has the required Google Play Services installed.
Use the GoogleSignIn.signInAsync method to prompt the user to sign in with their Google account.
Once the user has signed in, you can use the accessToken and refreshToken properties of the returned object to make authorized requests to the Google APIs.
The code lines for the above steps are:
import { GoogleSignIn } from 'expo-google-sign-in';
// Initialize the Google Sign-In process
await GoogleSignIn.initAsync({
// Your config. values
});
// Check if the device has the required Google Play Services installed
const isPlayServicesAvailable = await GoogleSignIn.askForPlayServicesAsync();
if (!isPlayServicesAvailable) {
console.error('Google Play services are not available on this device.');
return;
}
// Prompt the user to sign in with their Google account
const { accessToken, refreshToken } = await GoogleSignIn.signInAsync();
The auth0 documentation on their credentials manager states
The credentials manager is an easy to use source of Keychain-based
authentication for iOS and Android, and should be usable with
auth.credentialsManager
When trying to use this suggested method
const isLoggedIn = await auth0.credentialsManager.hasValidCredentials();
This error is being thrown
undefined is not an object (evaluating '_$$_REQUIRE(_dependencyMap[10],
"../context/actions/authActions").auth0.credentialsManager.getCredentials')
Here's an overview of our auth0 configuration, and how it works currently
in AuthActions.js
export const auth0 = new Auth0({
domain: Config.AUTH0_DOMAIN,
clientId: Config.AUTH0_CLIENT_ID,
});
export const actionLogin = async (callback) => {
try {
const authState = await auth0.webAuth.authorize({
scope: 'openid profile email offline_access',
audience: Config.AUTH0_AUDIENCE,
prompt: 'login',
});
let response = await getState(authState, callback);
return response
} catch (e) {
console.log('Error Authenticating: ', e)
}
The hasValidCredentials() method mentioned above is called after a user has successfully authenticated with the webAuth, and it should be returning something along the lines of an access token, refresh token, id, and email per the docs
Note that we are trying to use this so that we can stop using the react-native-keychain package and use auth0's implementation of the native keystores by
await auth0.credentialsManager.requireLocalAuthentication();
We are using Cognito Hosted UI with Local Account login and few SAML providers. There is an option to customize some styling but I am looking for couple of additional things.
Adding a custom text and link to an external site - like terms and conditions
SAML provider name do not take a space. I cannot show something like "Login with X".
Is there a way to customize the Hosted UI to do these things? Thanks.
You would be better off not using Cognito provided UI, and only use Cognito for authentication and authorization.
My react web application interact with Cognito, without using any of its provided UI components. Only through this way, you are able to have absolute control of your frontend.
You can refer below sample code, and use it for your web application.
Sample code
The sample code shows you how to signin by calling Cognito with using amazon-cognito-identity-js. Or some other reference: Implementing AWS-Cognito in Angular 2 and Using AWS Cognito in a Lambda function with npm
import {
CognitoUserPool,
CognitoUser,
CognitoUserSession,
CognitoUserAttribute,
AuthenticationDetails,
} from "amazon-cognito-identity-js";
export function signIn(email: string, password: string) {
var authenticationData = {
Username: email,
Password: password,
};
const cognitoUser = new CognitoUser({
Username: email,
Pool: getPool(
process.env.REACT_APP_BUSINESS_ACCOUNT_USER_POOL_ID!,
process.env.REACT_APP_BUSINESS_ACCOUNT_USER_POOL_CLIENT_ID!
),
});
var authenticationDetails = new AuthenticationDetails(authenticationData);
return new Promise((resolve, reject) => {
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: (result) => resolve(result),
onFailure: (error) => reject(error),
newPasswordRequired: (userAttributes, requiredAttributes) => {
resolve({ needResetPassword: true, cognitoUser, userAttributes });
},
});
});
}
I’m working on a React Native app that loads the example.com in a webview. I was wondering if it’s safe to use postMessage to pass username and password to React Native from the React Native webview. Do you think it’s okay or it's not safe?
This is the web app(example.com) sending username and password to React Native
And this is React Native <WebView onMessage={m => this._onMessage(m)} /> to receive that username and password to Authenticate user.
Seems like it will work but not sure if it’s safe. If not, is there a way to authenticate user in React Native from React Native webview?
According to MDN the postMessage method is safe, but please refer to security concern section.
You might send user session instead of username/password pair.
Your code would look like this:
// in webb app
const sendSession = async() => {
const session = await Auth.currentSession()
const data = {
idToken: session.getIdToken().getJwtToken(),
accessToken: session.getAccessToken().getJwtToken(),
refreshToken: session.getRefreshToken().getToken(),
}
window.ReactNativeWebView?.postMessage(JSON.stringify(data))
}
// in mobile app
const _onMessage = (event) => {
const data = JSON.parse(event.nativeEvent.data)
const {
idToken,
accessToken,
refreshToken
} = data
const userPool = new CognitoUserPool({
UserPoolId: 'UserPoolId',
ClientId: 'ClientId',
})
const cognitoIdToken = new CognitoIdToken({
IdToken: idToken,
})
const cognitoAccessToken = new CognitoAccessToken({
AccessToken: accessToken,
})
const cognitoRefreshToken = new CognitoRefreshToken({
RefreshToken: refreshToken,
})
const username = cognitoIdToken.payload['cognito:username'] // or whatever you need to get from playload as a username
const user = new CognitoUser({
Username: username,
Pool: userPool,
})
user.setSignInUserSession(
new CognitoUserSession({
AccessToken: cognitoAccessToken,
IdToken: cognitoIdToken,
RefreshToken: cognitoRefreshToken,
}),
)
}
I've followed this post on Medium to try to implement firebase authentication: https://blog.invertase.io/getting-started-with-firebase-authentication-on-react-native-a1ed3d2d6d91
I firstly implemented the Social Authentication with Facebook and it worked fine, but after that, when I successfully finished the Google Authentication part, the Facebook part was stuck at the line
return firebase.auth().signInWithCredential(credential);
Because It didn't return the user Promise after that so the Firebase onAuthStateChanged cannot log the user in
The function that handle facebook authentication:
onLoginOrRegisterFacebook = () => {
LoginManager.logInWithReadPermissions(["public_profile", "email"])
.then(result => {
if (result.isCancelled) {
return Promise.reject(new Error("The user cancelled the request"));
}
// Retrieve the access token
return AccessToken.getCurrentAccessToken();
})
.then(data => {
// Create a new Firebase credential with the token
const credential = firebase.auth.FacebookAuthProvider.credential(
data.accessToken
);
// Login with the credential
return firebase.auth().signInWithCredential(credential);
})
.then(user => {
})
.catch(error => {
const { code, message } = error;
});
};
The Facebook Sign In Method in Google Firebase Authentication Console is also enabled with enough information of my FB app, I also paste the OAuth redirect URI to my Facebook app configuration, and the Dashboard of Facebook Developer also successfully shows the installed app user.