Skip to content

Commit

Permalink
[C-3842] Add feature flag for native TikTok auth (#7634)
Browse files Browse the repository at this point in the history
  • Loading branch information
sliptype authored Feb 16, 2024
1 parent efdb27e commit bf4cd5f
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 88 deletions.
6 changes: 4 additions & 2 deletions packages/common/src/services/remote-config/feature-flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ export enum FeatureFlags {
BUY_WITH_COINFLOW = 'buy_with_coinflow',
EDIT_ALBUMS = 'edit_albums',
LOSSLESS_DOWNLOADS_ENABLED = 'lossless_downloads_enabled',
COINFLOW_OFFRAMP_ENABLED = 'coinflow_offramp_enabled'
COINFLOW_OFFRAMP_ENABLED = 'coinflow_offramp_enabled',
TIKTOK_NATIVE_AUTH = 'tiktok_native_auth'
}

type FlagDefaults = Record<FeatureFlags, boolean>
Expand Down Expand Up @@ -132,5 +133,6 @@ export const flagDefaults: FlagDefaults = {
[FeatureFlags.BUY_WITH_COINFLOW]: false,
[FeatureFlags.EDIT_ALBUMS]: false,
[FeatureFlags.LOSSLESS_DOWNLOADS_ENABLED]: false,
[FeatureFlags.COINFLOW_OFFRAMP_ENABLED]: false
[FeatureFlags.COINFLOW_OFFRAMP_ENABLED]: false,
[FeatureFlags.TIKTOK_NATIVE_AUTH]: true
}
178 changes: 92 additions & 86 deletions packages/mobile/src/hooks/useTikTokAuth.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createUseTikTokAuthHook } from '@audius/common/hooks'
import { createUseTikTokAuthHook, useFeatureFlag } from '@audius/common/hooks'
import type { UseTikTokAuthArguments, Credentials } from '@audius/common/hooks'
import { FeatureFlags } from '@audius/common/services'
import AsyncStorage from '@react-native-async-storage/async-storage'
import CookieManager from '@react-native-cookies/cookies'
import { Linking } from 'react-native'
Expand All @@ -22,109 +23,114 @@ const canOpenTikTok = () => {
return Linking.canOpenURL('tiktok://app')
}

const authenticate = async (): Promise<Credentials> => {
track(
make({
eventName: EventNames.TIKTOK_START_OAUTH
})
)

// Perform WebView auth if TikTok is not installed
// TikTok LoginKit is supposed to handle this but it doesn't seem to work
if (!(await canOpenTikTok())) {
return new Promise((resolve, reject) => {
dispatch(
oauthActions.requestNativeOpenPopup(
resolve,
reject,
authenticationUrl,
Provider.TIKTOK
const createAuthenticate =
(isNativeTikTokAuthEnabled: boolean) => async (): Promise<Credentials> => {
track(
make({
eventName: EventNames.TIKTOK_START_OAUTH
})
)

// Perform WebView auth if TikTok is not installed
// TikTok LoginKit is supposed to handle this but it doesn't seem to work
if (!(await canOpenTikTok()) || !isNativeTikTokAuthEnabled) {
return new Promise((resolve, reject) => {
dispatch(
oauthActions.requestNativeOpenPopup(
resolve,
reject,
authenticationUrl,
Provider.TIKTOK
)
)
)
})
}

tikTokInit(env.TIKTOK_APP_ID!)
})
}

return new Promise((resolve, reject) => {
let authDone = false
tikTokInit(env.TIKTOK_APP_ID!)

const handleTikTokAuth = async (
code: string,
error: boolean | null,
errorMessage: string
) => {
if (authDone) {
console.warn('TikTok auth already completed')
return
}
return new Promise((resolve, reject) => {
let authDone = false

const handleTikTokAuth = async (
code: string,
error: boolean | null,
errorMessage: string
) => {
if (authDone) {
console.warn('TikTok auth already completed')
return
}

if (error) {
return reject(new Error(errorMessage))
}
if (error) {
return reject(new Error(errorMessage))
}

// Need to set a csrf cookie because it is required for web
await CookieManager.set(env.IDENTITY_SERVICE!, {
name: 'csrfState',
value: 'true'
})
// Need to set a csrf cookie because it is required for web
await CookieManager.set(env.IDENTITY_SERVICE!, {
name: 'csrfState',
value: 'true'
})

try {
const response = await fetch(
`${env.IDENTITY_SERVICE}/tiktok/access_token`,
{
credentials: 'include',
method: 'POST',
body: JSON.stringify({
code,
state: 'true'
}),
headers: {
'Content-Type': 'application/json'
try {
const response = await fetch(
`${env.IDENTITY_SERVICE}/tiktok/access_token`,
{
credentials: 'include',
method: 'POST',
body: JSON.stringify({
code,
state: 'true'
}),
headers: {
'Content-Type': 'application/json'
}
}
)

if (!response.ok) {
return reject(
new Error(response.status + ' ' + (await response.text()))
)
}
)

if (!response.ok) {
return reject(
new Error(response.status + ' ' + (await response.text()))
)
}
const {
data: { access_token, open_id, expires_in }
} = await response.json()

const {
data: { access_token, open_id, expires_in }
} = await response.json()
track(
make({
eventName: EventNames.TIKTOK_COMPLETE_OAUTH
})
)

track(
make({
eventName: EventNames.TIKTOK_COMPLETE_OAUTH
authDone = true
return resolve({
accessToken: access_token,
openId: open_id,
expiresIn: expires_in
})
)

authDone = true
return resolve({
accessToken: access_token,
openId: open_id,
expiresIn: expires_in
})
} catch (e) {
return reject(e)
} catch (e) {
return reject(e)
}
}
}

// Needed for Android
const listener = tikTokEvents.addListener('onAuthCompleted', (resp) => {
listener?.remove()
handleTikTokAuth(resp.code, !!resp.status, resp.status)
})
// Needed for Android
const listener = tikTokEvents.addListener('onAuthCompleted', (resp) => {
listener?.remove()
handleTikTokAuth(resp.code, !!resp.status, resp.status)
})

tikTokAuth(handleTikTokAuth)
})
}
tikTokAuth(handleTikTokAuth)
})
}

export const useTikTokAuth = (args: UseTikTokAuthArguments) => {
const { isEnabled: isTikTokNativeAuthEnabled } = useFeatureFlag(
FeatureFlags.TIKTOK_NATIVE_AUTH
)

return createUseTikTokAuthHook({
authenticate,
authenticate: createAuthenticate(isTikTokNativeAuthEnabled),
handleError: (e: Error) => {
track(
make({
Expand Down

0 comments on commit bf4cd5f

Please sign in to comment.