import { H2, H4 } from '@expo/html-elements';
import * as AuthSession from 'expo-auth-session';
import { useAuthRequest } from 'expo-auth-session';
import * as FacebookAuthSession from 'expo-auth-session/providers/facebook';
import * as GoogleAuthSession from 'expo-auth-session/providers/google';
import Constants, { ExecutionEnvironment } from 'expo-constants';
import { maybeCompleteAuthSession } from 'expo-web-browser';
import React from 'react';
import { Platform, ScrollView, View } from 'react-native';
import { AuthSection } from './AuthResult';
import { getGUID } from '../../api/guid';
import TitledPicker from '../../components/TitledPicker';
import TitledSwitch from '../../components/TitledSwitch';
maybeCompleteAuthSession();
const isInClient = Constants.executionEnvironment === ExecutionEnvironment.StoreClient;
const languages = [
{ key: 'en', value: 'English' },
{ key: 'pl', value: 'Polish' },
{ key: 'nl', value: 'Dutch' },
{ key: 'fi', value: 'Finnish' },
];
const PROJECT_NAME_FOR_PROXY = '@community/native-component-list';
export default function AuthSessionScreen() {
const [usePKCE, setPKCE] = React.useState(true);
const [prompt, setSwitch] = React.useState(undefined);
const [language, setLanguage] = React.useState(languages[0].key);
return (
Settings
setSwitch(value ? AuthSession.Prompt.SelectAccount : undefined)}
/>
ID: {PROJECT_NAME_FOR_PROXY}
Services
);
}
AuthSessionScreen.navigationOptions = {
title: 'AuthSession',
};
function AuthSessionProviders(props: {
usePKCE: boolean;
prompt?: AuthSession.Prompt;
language: string;
}) {
const { usePKCE, prompt, language } = props;
const redirectUri = AuthSession.makeRedirectUri({
path: 'redirect',
preferLocalhost: Platform.select({ android: false, default: true }),
});
const options = {
usePKCE,
prompt,
redirectUri,
language,
};
const providers = [
Google,
GoogleFirebase,
Facebook,
Imgur,
Spotify,
Strava,
Twitch,
Dropbox,
Reddit,
Github,
Coinbase,
Uber,
Slack,
FitBit,
Okta,
Identity,
// Azure,
];
return (
{providers.map((Provider, index) => (
))}
);
}
function Google({ prompt, language, usePKCE }: any) {
const [request, result, promptAsync] = GoogleAuthSession.useAuthRequest(
{
language,
expoClientId: '629683148649-qevd4mfvh06q14i4nl453r62sgd1p85d.apps.googleusercontent.com',
clientId: `${getGUID()}.apps.googleusercontent.com`,
selectAccount: !!prompt,
usePKCE,
},
{
path: 'redirect',
preferLocalhost: true,
}
);
React.useEffect(() => {
if (request && result?.type === 'success') {
console.log('Result: ', result.authentication);
}
}, [result]);
return ;
}
function GoogleFirebase({ prompt, language, usePKCE }: any) {
const [request, result, promptAsync] = GoogleAuthSession.useIdTokenAuthRequest(
{
language,
expoClientId: '629683148649-qevd4mfvh06q14i4nl453r62sgd1p85d.apps.googleusercontent.com',
clientId: `${getGUID()}.apps.googleusercontent.com`,
selectAccount: !!prompt,
usePKCE,
},
{
path: 'redirect',
preferLocalhost: true,
}
);
React.useEffect(() => {
if (request && result?.type === 'success') {
console.log('Result:', result.params.id_token);
}
}, [result]);
return (
);
}
// Couldn't get this working. API is really confusing.
// function Azure({ useProxy, prompt, usePKCE }: any) {
// const redirectUri = AuthSession.makeRedirectUri({
// path: 'redirect',
// preferLocalhost: true,
// useProxy,
// native: Platform.select({
// ios: 'msauth.dev.expo.Payments://auth',
// android: 'msauth://dev.expo.payments/sZs4aocytGUGvP1%2BgFAavaPMPN0%3D',
// }),
// });
// // 'https://login.microsoftonline.com/your-tenant-id/v2.0',
// const discovery = AuthSession.useAutoDiscovery(
// 'https://login.microsoftonline.com/f8cdef31-a31e-4b4a-93e4-5f571e91255a/v2.0'
// );
// const [request, result, promptAsync] = useAuthRequest(
// // config
// {
// clientId: '96891596-721b-4ae1-8e67-674809373165',
// redirectUri,
// prompt,
// extraParams: {
// domain_hint: 'live.com',
// },
// // redirectUri: 'msauth.{bundleId}://auth',
// scopes: ['openid', 'profile', 'email', 'offline_access'],
// usePKCE,
// },
// // discovery
// discovery
// );
// return (
//
// );
// }
function Okta({ redirectUri, usePKCE }: any) {
const discovery = AuthSession.useAutoDiscovery('https://dev-720924.okta.com/oauth2/default');
const [request, result, promptAsync] = useAuthRequest(
{
clientId: '0oa4su9fhp4F2F4Eg4x6',
redirectUri,
scopes: ['openid', 'profile'],
usePKCE,
},
discovery
);
return ;
}
// Reddit only allows one redirect uri per client Id
// We'll only support bare, and proxy in this example
// If the redirect is invalid with http instead of https on web, then the provider
// will let you authenticate but it will redirect with no data and the page will appear broken.
function Reddit({ redirectUri, prompt, usePKCE }: any) {
let clientId: string;
if (isInClient) {
clientId = 'CPc_adCUQGt9TA';
} else {
if (Platform.OS === 'web') {
// web apps with uri scheme `https://localhost:19006`
clientId = '9k_oYNO97ly-5w';
} else {
// Native bare apps with uri scheme `bareexpo`
clientId = '2OFsAA7h63LQJQ';
}
}
const [request, result, promptAsync] = useAuthRequest(
{
clientId,
clientSecret: '',
redirectUri,
prompt,
scopes: ['identity'],
usePKCE,
},
{
authorizationEndpoint: 'https://www.reddit.com/api/v1/authorize.compact',
tokenEndpoint: 'https://www.reddit.com/api/v1/access_token',
}
);
return ;
}
// Imgur Docs https://api.imgur.com/oauth2
// Create app https://api.imgur.com/oauth2/addclient
function Imgur({ redirectUri, prompt, usePKCE }: any) {
let clientId: string;
if (isInClient) {
// Normalize the host to `localhost` for other testers
// Expects: exp://127.0.0.1:19000/--/redirect
clientId = '7ab2f3cc75427a0';
} else {
if (Platform.OS === 'web') {
// web apps with uri scheme `https://localhost:19006`
clientId = '181b22d17a3743e';
} else {
// Native bare apps with uri scheme `bareexpo`
clientId = 'd839d91135a16cc';
}
}
const [request, result, promptAsync] = useAuthRequest(
{
clientId,
responseType: AuthSession.ResponseType.Token,
redirectUri,
scopes: [],
usePKCE,
prompt,
},
// discovery
{
authorizationEndpoint: 'https://api.imgur.com/oauth2/authorize',
tokenEndpoint: 'https://api.imgur.com/oauth2/token',
}
);
return (
promptAsync({
windowFeatures: { width: 500, height: 750 },
})
}
/>
);
}
// TODO: Add button to test using an invalid redirect URI. This is a good example of AuthError.
// Works for all platforms
function Github({ redirectUri, prompt, usePKCE }: any) {
let clientId: string;
if (isInClient) {
clientId = '7eb5d82d8f160a434564';
} else {
if (Platform.OS === 'web') {
// web apps
clientId = 'fd9b07204f9d325e8f0e';
} else {
// Native bare apps with uri scheme `bareexpo`
clientId = '498f1fae3ae16f066f34';
}
}
const [request, result, promptAsync] = useAuthRequest(
{
clientId,
redirectUri,
scopes: ['identity'],
usePKCE,
prompt,
},
// discovery
{
authorizationEndpoint: 'https://github.com/login/oauth/authorize',
tokenEndpoint: 'https://github.com/login/oauth/access_token',
revocationEndpoint:
'https://github.com/settings/connections/applications/d529db5d7d81c2d50adf',
}
);
return (
promptAsync({
windowFeatures: { width: 500, height: 750 },
})
}
/>
);
}
// I couldn't get access to any scopes
// This never returns to the app after authenticating
function Uber({ redirectUri, prompt, usePKCE }: any) {
// https://developer.uber.com/docs/riders/guides/authentication/introduction
const [request, result, promptAsync] = useAuthRequest(
{
clientId: 'kTpT4xf8afVxifoWjx5Nhn-IFamZKp2x',
redirectUri,
scopes: [],
usePKCE,
prompt,
// Enable to test invalid_scope error
// scopes: ['invalid'],
},
// discovery
{
authorizationEndpoint: 'https://login.uber.com/oauth/v2/authorize',
tokenEndpoint: 'https://login.uber.com/oauth/v2/token',
revocationEndpoint: 'https://login.uber.com/oauth/v2/revoke',
}
);
return ;
}
// https://dev.fitbit.com/apps/new
// Easy to setup
// Only allows one redirect URI per app (clientId)
// Refresh doesn't seem to return a new access token :[
function FitBit({ redirectUri, prompt, usePKCE }: any) {
let clientId: string;
if (isInClient) {
// Client without proxy
clientId = '22BNXX';
} else {
if (Platform.OS === 'web') {
// web apps with uri scheme `https://localhost:19006`
clientId = '22BNXQ';
} else {
// Native bare apps with uri scheme `bareexpo`
clientId = '22BGYS';
}
}
const [request, result, promptAsync] = useAuthRequest(
{
clientId,
redirectUri,
scopes: ['activity', 'sleep'],
prompt,
usePKCE,
},
// discovery
{
authorizationEndpoint: 'https://www.fitbit.com/oauth2/authorize',
tokenEndpoint: 'https://api.fitbit.com/oauth2/token',
revocationEndpoint: 'https://api.fitbit.com/oauth2/revoke',
}
);
return ;
}
function Facebook({ usePKCE, language }: any) {
const [request, result, promptAsync] = FacebookAuthSession.useAuthRequest(
{
clientId: '145668956753819',
usePKCE,
language,
scopes: ['user_likes'],
},
{
path: 'redirect',
preferLocalhost: true,
}
);
// Add fetch user example
return (
);
}
function Slack({ redirectUri, prompt, usePKCE }: any) {
// https://api.slack.com/apps
// After you created an app, navigate to [Features > OAuth & Permissions]
// - Add a redirect URI Under [Redirect URLs]
// - Under [Scopes] add the scopes you want to request from the user
// Next go to [App Credentials] to get your client ID and client secret
// No refresh token or expiration is returned, assume the token lasts forever.
const [request, result, promptAsync] = useAuthRequest(
// config
{
clientId: '58692702102.1023025401076',
redirectUri,
scopes: ['emoji:read'],
prompt,
usePKCE,
},
// discovery
{
authorizationEndpoint: 'https://slack.com/oauth/authorize',
tokenEndpoint: 'https://slack.com/api/oauth.access',
}
);
return ;
}
// Works on all platforms
function Spotify({ redirectUri, prompt, usePKCE }: any) {
const [request, result, promptAsync] = useAuthRequest(
{
clientId: 'a946eadd241244fd88d0a4f3d7dea22f',
redirectUri,
scopes: ['user-read-email', 'playlist-modify-public', 'user-read-private'],
usePKCE,
extraParams: {
show_dialog: 'false',
},
prompt,
},
// discovery
{
authorizationEndpoint: 'https://accounts.spotify.com/authorize',
tokenEndpoint: 'https://accounts.spotify.com/api/token',
}
);
return (
);
}
function Strava({ redirectUri, prompt, usePKCE }: any) {
const discovery = {
authorizationEndpoint: 'https://www.strava.com/oauth/mobile/authorize',
tokenEndpoint: 'https://www.strava.com/oauth/token',
};
const [request, result, promptAsync] = useAuthRequest(
{
clientId: '51935',
redirectUri,
scopes: ['activity:read_all'],
usePKCE,
prompt,
},
discovery
);
React.useEffect(() => {
if (request && result?.type === 'success' && result.params.code) {
AuthSession.exchangeCodeAsync(
{
clientId: request?.clientId,
redirectUri,
code: result.params.code,
extraParams: {
// You must use the extraParams variation of clientSecret.
client_secret: `...`,
},
},
discovery
).then((result) => {
console.log('RES: ', result);
});
}
}, [result]);
return ;
}
// Works on all platforms
function Identity({ redirectUri, prompt }: any) {
const discovery = AuthSession.useAutoDiscovery('https://demo.identityserver.io');
const [request, result, promptAsync] = useAuthRequest(
{
clientId: 'native.code',
redirectUri,
prompt,
scopes: ['openid', 'profile', 'email', 'offline_access'],
},
discovery
);
return (
);
}
// Doesn't work with proxy
function Coinbase({ redirectUri, prompt, usePKCE }: any) {
const [request, result, promptAsync] = useAuthRequest(
{
clientId: '13b2bc8d9114b1cb6d0132cf60c162bc9c2d5ec29c2599003556edf81cc5db4e',
redirectUri,
prompt,
usePKCE,
scopes: ['wallet:accounts:read'],
},
// discovery
{
authorizationEndpoint: 'https://www.coinbase.com/oauth/authorize',
tokenEndpoint: 'https://api.coinbase.com/oauth/token',
revocationEndpoint: 'https://api.coinbase.com/oauth/revoke',
}
);
return (
);
}
function Dropbox({ redirectUri, prompt, usePKCE }: any) {
const [request, result, promptAsync] = useAuthRequest(
{
clientId: 'pjvyj0c5kxxrsfs',
redirectUri,
prompt,
usePKCE,
scopes: [],
responseType: AuthSession.ResponseType.Token,
},
// discovery
{
authorizationEndpoint: 'https://www.dropbox.com/oauth2/authorize',
tokenEndpoint: 'https://www.dropbox.com/oauth2/token',
}
);
return (
);
}
function Twitch({ redirectUri, prompt, usePKCE }: any) {
const [request, result, promptAsync] = useAuthRequest(
{
clientId: 'r7jomrc4hiz5wm1wgdzmwr1ccb454h',
redirectUri,
prompt,
scopes: ['openid', 'user_read', 'analytics:read:games'],
usePKCE,
},
{
authorizationEndpoint: 'https://id.twitch.tv/oauth2/authorize',
tokenEndpoint: 'https://id.twitch.tv/oauth2/token',
revocationEndpoint: 'https://id.twitch.tv/oauth2/revoke',
}
);
return ;
}