Open mpark1 opened 1 month ago
hello @mpark1 . Apologies for any inconvenience using the library. Could you please share your amplify configuration ? Please refrain from adding any sensitive values.
Can you also manually configure Amplify and see if you are getting the same issue ?
@mpark1 to tag onto @israx's requests above, can you also share more of your frontend code that's part of your Auth flow as well as if there's any associated errors in the network tab or console?
@israx @cwomack I’ll follow up with the amplify configuration and the Auth flow and the code in couple hours. In the meantime, I want to clarify how signinwithredirect should work.
Currently, I’m using Cognito for user sign up. My understanding was that when a user who hasn’t signed up on app should be able to click on ‘sign in with apple’ button which then should show the the apple sign in page -> user signs in and we get back the information about this apple account -> then that somehow signs up the user at the same time and automatically creates a user in my Cognito user pool. Is there some loophole in my understanding of how signinwithredirect works? I ask because I’m thinking maybe the user needs to sign up first through regular email and password option with Cognito, only then they should be able to sign in with idp.
@israx Could you clarify what is meant by manually configuring Amplify? Do you mean using Amplify CLI?
Here's what I've attempted thus far:
I followed everything step by step as illustrated in the official doc (https://docs.amplify.aws/gen1/react-native/build-a-backend/auth/add-social-provider/) except the section for Add custom state and Custom providers. Then when running my project with the code I shared, when I click on the apple sign-in on the front-end, the app simulator launches an in app browser but it does not take me to the apple sign-in page which I believe is because of the url missing my app's name in the beginning. I'll point out that the only difference I had from the official doc was that I used amplify update auth to add apple sign in.
So I tried going into the AWS Cognito console and updated Sign In Experience by adding Federated sign in, just filling in all the items. I've double checked that I filled them with the correct team id, service id, key, etc obtained from Apple developers site. But nothing changed.
I went into Amplify studio's Authentication section and repeated step 2.
Then I tried with the Amplify CLI again, with amplify update auth, which strangely this time, asked me to provide team id, service id, key etc, although I've already filled in the same information from Cognito console.
I've run amplify push/pull every time I was updating the configuration with CLI.
Below is my cli-inputs.json
{ "version": "1", "cognitoConfig": { "identityPoolName": "milkywayXXXXXXXX_identitypool_XXXXXXXX", "allowUnauthenticatedIdentities": false, "resourceNameTruncated": "milkywXXXXXXXX", "userPoolName": "milkywayXXXXXXXX_userpool_XXXXXXXX", "autoVerifiedAttributes": [ "email" ], "mfaConfiguration": "OFF", "mfaTypes": [ "SMS Text Message" ], "smsAuthenticationMessage": "Your authentication code is {####}", "smsVerificationMessage": "Your verification code is {####}", "emailVerificationSubject": "Your verification code", "emailVerificationMessage": "Your verification code is {####}", "defaultPasswordPolicy": false, "passwordPolicyMinLength": 8, "passwordPolicyCharacters": [], "requiredAttributes": [ "email" ], "aliasAttributes": [], "userpoolClientGenerateSecret": false, "userpoolClientRefreshTokenValidity": 30, "userpoolClientWriteAttributes": [], "userpoolClientReadAttributes": [], "userpoolClientLambdaRole": "milkywXXXXXXXX_userpoolclient_lambda_role", "userpoolClientSetAttributes": false, "sharedId": "b2dd3f99", "resourceName": "milkywayXXXXXXXX", "authSelections": "identityPoolAndUserPool", "useDefault": "defaultSocial", "usernameAttributes": [ "email" ], "userPoolGroupList": [ "admin" ], "serviceName": "Cognito", "usernameCaseSensitive": false, "useEnabledMfas": true, "authRoleArn": { "Fn::GetAtt": [ "AuthRole", "Arn" ] }, "unauthRoleArn": { "Fn::GetAtt": [ "UnauthRole", "Arn" ] }, "breakCircularDependency": true, "dependsOn": [], "userPoolGroups": false, "adminQueries": false, "hostedUI": true, "hostedUIDomainName": "milkywayXXXXXXXX-XXXXXXXX", "authProvidersUserPool": [ "SignInWithApple" ], "hostedUIProviderMeta": "[{\"ProviderName\":\"SignInWithApple\",\"authorize_scopes\":\"email\",\"AttributeMapping\":{\"email\":\"email\"}}]", "oAuthMetadata": "{\"AllowedOAuthFlows\":[\"code\"],\"AllowedOAuthScopes\":[\"phone\",\"email\",\"openid\",\"profile\",\"aws.cognito.signin.user.admin\"],\"CallbackURLs\":[\"milkyway://\"],\"LogoutURLs\":[\"milkyway://\"]}", "authProviders": [] } }
When calling signInWithRedirect, the browser tries to open ap-northeast-2.amazoncognito.com which obviously doesn't exist on the server. I would have expected something like hostedUIDomainName.auth to be prepended but is missing in the url that should take me to the apple sign in page.
Attaching simulator screenshot for reference:
Update: Strangely, when running on emulator, I see that the redirect url is https://milkyway xxxxxxxx-XXXXXXXX-dev.auth.ap-northeast-2.amazoncognito.com/oauth2/authorize?redirect_uri=milkyway%3A%2F%2F&response_type=code&client_id=xxx&identity_provider=SignInWithApple&scope=phone%20email%20openid%20profile%20aws.cognito.signin.user.admin&state=xxx&code_challenge=xxx&code_challenge_method=S256 but even on the emulator the browser displays that the site can't be reached.
I was browsing through Amazon Cognito User pools App Integration where we can set up a domain, but regardless of setting the cognito domain or the custom domain there, I would be forced to use the hosted ui instead of my own sign in/sign up ui, right? I'm clueless at this point what could have gone wrong in the configuration.
@cwomack I have a fairly simple Auth flow. I've included the SignIn page's code but bascially it's the first screen when my app launches when no user is signed in. And to summarize what SignIn screen contains, it just have the sign in with apple button which opens the webview when pressed and I would have expected it take me to the Apple's sign-in page. Right now, I am able to get the webview to open on my simulator but it never reaches the Apple's sign-in page and I don't get any console logs. The webview just displays a message that it doesn't exist on the server because the url it gets is "ap-northeast-2.amazoncognito.come" so no information about my app's domain is included. Also I don't get any console log when the webview opens, but when I close it, I get "res from Apple sign in: undefined".
It should be clear from this code that I'm implementing my own ui for signin/signup and we're not wrapping our app with hosted ui for authentication provided by amplify. For instance, I've seen examples that uses import { Authenticator, useAuthenticator } from '@aws-amplify/ui-react-native';
and wraps the app with the Authenticator provider but we don't intend to use that. We also don't have any hub listeners set up anywhere in our project. Am I supposed to use Auth.federatedSignIn instead?
As far as I could gather from the official doc, using the Authenticator from @aws-amplify/ui-react-native shouldn't be necessary to implement social sign-in. Is this correct?
Index:
import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';
import {Amplify} from 'aws-amplify';
import config from './src/amplifyconfiguration.json';
import 'react-native-gesture-handler';
import {initializePushNotifications} from 'aws-amplify/push-notifications';
Amplify.configure(config);
initializePushNotifications();
AppRegistry.registerComponent(appName, () => App);
App.tsx:
const App = () => {
return (
<Provider store={store}>
<GestureHandlerRootView style={globalStyle.flex}>
<BottomSheetModalProvider>
<RootNavigation />
</BottomSheetModalProvider>
</GestureHandlerRootView>
</Provider>
);
};
SignIn code:
const SignIn = ({navigation}) => {
const dispatch = useDispatch();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const passwordRef = useRef(null);
const [isLoggingIn, setIsLoggingIn] = useState(false);
const onChangeEmail = useCallback(text => {
setEmail(text.trim());
}, []);
const onChangePassword = useCallback(text => {
setPassword(text.trim());
}, []);
const canGoNext = email && password;
const onSubmit = useCallback(async () => {
if (isLoggingIn) {
return;
}
try {
setIsLoggingIn(true);
const {isSignedIn, nextStep} = await signIn({
username: email,
password: password,
});
if (isSignedIn) {
const userId = await checkUser();
dispatch(setCognitoUsername(userId));
const response = await fetchUserFromDB(userId);
dispatch(
setOwnerDetails({
name: response.name,
email: response.email,
}),
);
}
// 미인증 계정 인증화면으로 보내기
if (nextStep.signInStep === 'CONFIRM_SIGN_UP') {
AlertBox('미인증 계정입니다', '', '인증하러가기', async () => {
await resendSignUpCode({username: email});
navigation.navigate('ConfirmAccount', {
name: '',
email: email,
purpose: 'reconfirm',
uri: '',
});
});
}
} catch (error) {
console.log('error signing in: ', error);
if (error.name === 'UserNotFoundException') {
// [UserNotFoundException: User does not exist.]
AlertBox(
'존재하지 않는 계정입니다.',
'회원가입을 진행해주세요.',
'확인',
() => {
setEmail('');
setPassword('');
},
);
} else if (error.name === 'NotAuthorizedException') {
// [NotAuthorizedException: Incorrect username or password.]
AlertBox(
'이메일 또는 비밀번호를 다시 확인해주세요.',
'',
'확인',
'none',
);
}
} finally {
setIsLoggingIn(false);
}
}, [isLoggingIn, email, password]);
const handleSignInWithApple = async () => {
try {
const resFromSignInWithApple = await signInWithRedirect({
provider: 'Apple',
});
console.log('res from Apple sign in: ', resFromSignInWithApple);
} catch (error) {
console.log('Error signing in with Apple');
}
};
const handleSignOut = async () => {
try {
await signOut();
} catch (error) {
console.log('error signing out: ', error);
}
};
const renderEmailField = () => {
return (
<Input
onChangeText={onChangeEmail}
value={email}
inputContainerStyle={[styles.inputContainerStyle, {marginBottom: -10}]}
inputStyle={styles.inputStyle}
label="이메일*"
labelStyle={styles.labelStyle}
placeholder="이메일을 입력해주세요"
placeholderTextColor={'#d9d9d9'}
onSubmitEditing={() => passwordRef.current?.focus()}
returnKeyType={'next'}
keyboardType={'email-address'}
/>
);
};
const renderPasswordField = () => {
return (
<Input
onChangeText={onChangePassword}
value={password}
ref={passwordRef}
inputContainerStyle={[styles.inputContainerStyle, {marginBottom: -23}]}
inputStyle={styles.inputStyle}
label="비밀번호*"
labelStyle={styles.labelStyle}
placeholder="비밀번호를 입력해주세요"
placeholderTextColor={'#d9d9d9'}
keyboardType={Platform.OS === 'android' ? 'default' : 'ascii-capable'}
secureTextEntry={true}
maxLength={256}
/>
);
};
const [checked, setChecked] = useState(true);
const toggleCheckbox = () => setChecked(!checked);
const renderAutoLogin = () => {
return (
<CheckBox
title={'자동 로그인'}
wrapperStyle={{marginLeft: -15}}
textStyle={styles.checkBox.text}
checked={checked}
onPress={toggleCheckbox}
iconType="material-community"
checkedIcon={
<Icon
name="checkbox-outline"
type="material-community"
color="#6395E1"
size={25}
iconStyle={{marginRight: -3}}
/>
}
uncheckedIcon={
<Icon
name="checkbox-blank-outline"
type="material-community"
color="#939393"
size={25}
iconStyle={{marginRight: -3}}
/>
}
/>
);
};
const renderButtons = () => {
return (
<>
<Button
title={'로그인'}
titleStyle={styles.loginButton.title}
containerStyle={styles.loginButton.container}
buttonStyle={globalStyle.backgroundBlue}
disabled={!canGoNext}
onPress={() => onSubmit()}
/>
<Button
title={'회원가입'}
titleStyle={styles.signUpButton.title}
type={'outline'}
buttonStyle={styles.signUpButton.button}
containerStyle={styles.signUpButton.container}
onPress={() => navigation.navigate('SignUp')}
/>
</>
);
};
const renderFindPasswordButton = () => {
return (
<Pressable onPress={() => navigation.navigate('ForgotPassword')}>
<Text style={styles.findPassWordText}>비밀번호 찾기</Text>
</Pressable>
);
};
const renderAppleSignInSignOutButtons = () => {
return (
<>
<Pressable onPress={() => handleSignInWithApple()}>
<Text style={styles.findPassWordText}>Sign in with Apple</Text>
</Pressable>
<Pressable onPress={() => handleSignOut()}>
<Text style={styles.findPassWordText}>Sign out (Apple)</Text>
</Pressable>
</>
);
};
return (
<SafeAreaView style={[globalStyle.backgroundWhite, globalStyle.flex]}>
<Text style={styles.appName}>은하수</Text>
<View style={styles.spacer}>
{renderEmailField()}
{renderPasswordField()}
{renderAutoLogin()}
{renderButtons()}
{renderFindPasswordButton()}
{renderAppleSignInSignOutButtons()}
</View>
</SafeAreaView>
);
};
@israx Here's my latest amplifyconfiguration.json:
{
"aws_project_region": "ap-northeast-2",
"aws_mobile_analytics_app_id": "xxx",
"aws_mobile_analytics_app_region": "ap-northeast-2",
"Analytics": {
"AWSPinpoint": {
"appId": "xxxf",
"region": "ap-northeast-2"
}
},
"aws_appsync_graphqlEndpoint": "https://xxx.appsync-api.ap-northeast-2.amazonaws.com/graphql",
"aws_appsync_region": "ap-northeast-2",
"aws_appsync_authenticationType": "API_KEY",
"aws_appsync_apiKey": "xxx",
"aws_cognito_identity_pool_id": "ap-northeast-2:xxx",
"aws_cognito_region": "ap-northeast-2",
"aws_user_pools_id": "ap-northeast-2_SpQOUxXXq",
"aws_user_pools_web_client_id": "xxx",
"oauth": {
"domain": "milkywayXXXXXXXX-XXXXXXXX-dev.auth.ap-northeast-2.amazoncognito.com",
"scope": [
"phone",
"email",
"openid",
"profile",
"aws.cognito.signin.user.admin"
],
"redirectSignIn": "milkyway://",
"redirectSignOut": "milkyway://",
"responseType": "code"
},
"federationTarget": "COGNITO_USER_POOLS",
"aws_cognito_username_attributes": [
"EMAIL"
],
"aws_cognito_social_providers": [
"APPLE"
],
"aws_cognito_signup_attributes": [
"EMAIL"
],
"aws_cognito_mfa_configuration": "OFF",
"aws_cognito_mfa_types": [
"SMS"
],
"aws_cognito_password_protection_settings": {
"passwordPolicyMinLength": 8,
"passwordPolicyCharacters": []
},
"aws_cognito_verification_mechanisms": [
"EMAIL"
],
"aws_user_files_s3_bucket": "milkywaydeXXXXXX-dev",
"aws_user_files_s3_bucket_region": "ap-northeast-2"
}
Hello @mpark1 . Thank you for all the context. To answer a couple of your questions.
using the Authenticator from @aws-amplify/ui-react-native shouldn't be necessary to implement social sign-in. Is this correct?
You don't have to use @aws-amplify/ui-react-native
in order to implement social sign-in. As long as you are able to call the signInWithRedirect
API is okay.
Could you clarify what is meant by manually configuring Amplify?
Yeah in addition to configure Amplify using the CLI. You can pass your configuration as follows
Amplify.configure(aws-exports)
, or using a manual configuration
Amplify.configure({
Auth: {
Cognito: {
userPoolId: "xxxxx",
userPoolClientId: "xxxx",
identityPoolId: "xxxxx",
loginWith: {
oauth: {
domain: "your-domain",
redirectSignIn: ["milkyway://"],
redirectSignOut: ["milkyway://"],
responseType: "code",
scopes: [
"email",
"openid",
"profile",
"aws.cognito.signin.user.admin",
],}, }, }, }, }); })
Based on your dependencies I think you need to include the @aws-amplify/rtn-web-browser
dep as well. You can refer to this section. Let us know if after installing this package your social sign-in ends up working
@israx I do have @aws-amplify/rtn-web-browser in my project's root package.json which I didn't share here, but is there any other place that this dep should be included? I'm not sure if you meant this as the @aws-amplify/rtn-web-browser has to be included in the 'amplify' directory of my react native project.
The dep should be included in your root project, no in the amplify
directory. Could you please share your package.json
?
@israx Sure! Here's my root project package.json
{
"name": "MilkyWay",
"version": "0.0.1",
"private": true,
"scripts": {
"android": "react-native run-android",
"ios": "react-native run-ios",
"lint": "eslint .",
"start": "react-native start",
"test": "jest"
},
"dependencies": {
"@amuizz/read-more-text": "^1.1.2",
"@aws-amplify/cli": "^12.8.2",
"@aws-amplify/react-native": "^1.0.28",
"@aws-amplify/rtn-push-notification": "^1.2.28",
"@aws-amplify/rtn-web-browser": "^1.0.29",
"@bam.tech/react-native-image-resizer": "^3.0.7",
"@craftzdog/react-native-buffer": "^6.0.5",
"@gorhom/bottom-sheet": "^4.5.1",
"@react-native-async-storage/async-storage": "^1.23.1",
"@react-native-community/netinfo": "^11.1.1",
"@react-native-masked-view/masked-view": "^0.3.1",
"@react-native-seoul/kakao-login": "^5.4.1",
"@react-navigation/bottom-tabs": "^6.5.11",
"@react-navigation/core": "^6.4.10",
"@react-navigation/material-top-tabs": "^6.6.5",
"@react-navigation/native": "^6.1.9",
"@react-navigation/native-stack": "^6.9.17",
"@react-navigation/stack": "^6.3.20",
"@reduxjs/toolkit": "^2.0.1",
"@rneui/base": "^4.0.0-rc.7",
"@rneui/themed": "^4.0.0-rc.8",
"aws-amplify": "^6.2.0",
"aws-sdk": "^2.1513.0",
"react": "18.2.0",
"react-native": "0.72.7",
"react-native-date-picker": "^4.3.3",
"react-native-device-info": "^10.12.0",
"react-native-dropdown-picker": "^5.4.6",
"react-native-fs": "^2.20.0",
"react-native-gesture-handler": "^2.14.0",
"react-native-get-random-values": "^1.11.0",
"react-native-image-crop-picker": "^0.40.2",
"react-native-image-picker": "^7.0.3",
"react-native-keyboard-aware-scroll-view": "^0.9.5",
"react-native-pager-view": "^6.2.3",
"react-native-permissions": "^3.10.1",
"react-native-quick-base64": "^2.0.8",
"react-native-reanimated": "^3.6.1",
"react-native-safe-area-context": "^4.7.4",
"react-native-screens": "^3.27.0",
"react-native-tab-view": "^3.5.2",
"react-native-uuid": "^2.0.1",
"react-native-vector-icons": "^10.0.2",
"react-native-video": "^5.2.1",
"react-navigation-header-buttons": "^11.1.1",
"react-redux": "^9.0.2",
"redux": "^5.0.0"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@babel/preset-env": "^7.20.0",
"@babel/runtime": "^7.20.0",
"@react-native/eslint-config": "^0.72.2",
"@react-native/metro-config": "^0.72.11",
"@tsconfig/react-native": "^3.0.0",
"@types/react": "^18.0.24",
"@types/react-test-renderer": "^18.0.0",
"babel-jest": "^29.2.1",
"eslint": "^8.19.0",
"jest": "^29.2.1",
"metro-react-native-babel-preset": "0.76.8",
"prettier": "^2.4.1",
"react-test-renderer": "18.2.0",
"typescript": "4.8.4"
},
"engines": {
"node": ">=16"
}
}
I double checked the federated sign in provider (apple) settings from the AWS Cognito console matched with what I have on my Apple Developer's page (like team ID, service ID, etc). I tried testing sign in with apple on my iPhone as well as the simulator and it turns out it was grabbing the "expected" redirect url from signinwithredirect, yet even when testing on the actual device, I'm still getting the "server cannot be found error" with the @aws-amplify/rtn-web-browser dep there. The behavior is the same on android.
Looking at the source code for signinwithredirect and comparing it to the url returned from the api call,
https://milkywayxxxxxxxx-xxxxxxxx-dev.auth.ap-northeast-2.amazoncognito.com/oauth2/authorize?redirect_uri=milkyway%3A%2F%2F&response_type=code&client_id=xxx&identity_provider=SignInWithApple&scope=phone%20email%20openid%20profile%20aws.cognito.signin.user.admin&state=xxx&code_challenge=xxx&code_challenge_method=S256
I assumed this is the correct url. (FYI, the client_id param matched with my amplify web client id). At this point, I'm clueless as to what could have gone wrong on my end when I was setting things up. I thought maybe the Apple private key uploaded to Cognito was incorrect/corrupted so I reuploaded that, but the key isn't even necessary during the step where the user should first be taken to the apple's sign in page when the url returned from signinwithredirect is provided. Please advise.
@mpark1, are you able to ping your endpoint of https://milkywayxxxxxxxx-xxxxxxxx-dev.auth.ap-northeast-2.amazoncognito.com/oauth2/authorize?redirect_uri=milkyway%3A%2F%2F&response_type=code&client_id=xxx&identity_provider=SignInWithApple&scope=phone%20email%20openid%20profile%20aws.cognito.signin.user.admin&state=xxx&code_challenge=xxx&code_challenge_method=S256
and see if it results in any errors?
Before opening, please confirm:
JavaScript Framework
React Native
Amplify APIs
Authentication
Amplify Version
v6
Amplify Categories
auth
Backend
Amplify CLI
Environment information
Describe the bug
I am trying to implement sign in with apple functionality in my react native app. I've followed the instructions under the official doc's "Add social provider sign-in" section.
I believe to have configured everything as instructed in the doc on 1. Apple developers site and 2. From Amplify CLI through amplify update auth.
The code block shown is how I'm calling signInWithRedirect. I'll add that I'm not using the Hosted UI for sign in/sign up and have our own implementation of the sign in ui. We simply have a button that calls signInWithRedirect when pressed.
Now the issue we have is that when signInWithRedirect is called, the url it tries to open in the rtn-web-browser is invalid. For instance, I noticed that one of the aws config related files, we have 'hostedUI:true', and 'hostedUIdomain: appname....-dev.auth.location.com', so I'd expect that signInWithRedirect should receive a url that is "appname....-dev.auth.location.com", yet in our app, we keep getting just "location.com" i.e. something along this line but without the "appname..-dev.auth" prepended in front.
Is this because my app is not using the hosted ui for sign in? If that's the case, how are we supposed to implement sign in with apple? Lastly, I should point out that completing steps 1 and 2 didn't work so I ended up doing the same configuration from AWS Cognito console as well as within Amplify studio's Authentication tab and this seems to have caused some misalignment in the sign up password requirement, but regardless, there's definitely something wrong going on with the url that should prompt navigation to the apple sign in page.
Expected behavior
signInWithRedirect with provider Apple should generate a url that matches with the hosted ui domain which in turn will prompt navigation to the apple sign in page when everything is set up right.
Reproduction steps
Code Snippet
Log output
aws-exports.js
No response
Manual configuration
No response
Additional configuration
No response
Mobile Device
No response
Mobile Operating System
No response
Mobile Browser
No response
Mobile Browser Version
No response
Additional information and screenshots
No response