Update package dependencies and enhance Trakt authentication flow; add base64-js and react-native-url-polyfill to package.json, improve expo-auth-session integration with PKCE support in TraktSettingsScreen, and refactor token exchange method in traktService to include code verifier for enhanced security.

This commit is contained in:
tapframe 2025-04-26 15:45:49 +05:30
parent 78583c8e80
commit cc894ff4f4
4 changed files with 39 additions and 20 deletions

16
package-lock.json generated
View file

@ -24,6 +24,7 @@
"@types/lodash": "^4.17.16",
"@types/react-native-video": "^5.0.20",
"axios": "^1.8.4",
"base64-js": "^1.5.1",
"date-fns": "^4.1.0",
"eventemitter3": "^5.0.1",
"expo": "~52.0.43",
@ -40,7 +41,7 @@
"expo-screen-orientation": "~8.0.4",
"expo-status-bar": "~2.0.1",
"expo-system-ui": "^4.0.9",
"expo-web-browser": "^14.0.2",
"expo-web-browser": "~14.0.2",
"lodash": "^4.17.21",
"react": "18.3.1",
"react-native": "0.76.9",
@ -56,6 +57,7 @@
"react-native-screens": "~4.4.0",
"react-native-svg": "^15.11.2",
"react-native-tab-view": "^4.0.10",
"react-native-url-polyfill": "^2.0.0",
"react-native-video": "^6.12.0",
"react-native-web": "~0.19.13",
"subsrt": "^1.1.1"
@ -11047,6 +11049,18 @@
"react-native-pager-view": ">= 6.0.0"
}
},
"node_modules/react-native-url-polyfill": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/react-native-url-polyfill/-/react-native-url-polyfill-2.0.0.tgz",
"integrity": "sha512-My330Do7/DvKnEvwQc0WdcBnFPploYKp9CYlefDXzIdEaA+PAhDYllkvGeEroEzvc4Kzzj2O4yVdz8v6fjRvhA==",
"license": "MIT",
"dependencies": {
"whatwg-url-without-unicode": "8.0.0-3"
},
"peerDependencies": {
"react-native": "*"
}
},
"node_modules/react-native-vector-icons": {
"version": "10.2.0",
"resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-10.2.0.tgz",

View file

@ -25,11 +25,13 @@
"@types/lodash": "^4.17.16",
"@types/react-native-video": "^5.0.20",
"axios": "^1.8.4",
"base64-js": "^1.5.1",
"date-fns": "^4.1.0",
"eventemitter3": "^5.0.1",
"expo": "~52.0.43",
"expo-auth-session": "^6.0.3",
"expo-blur": "^14.0.3",
"expo-dev-client": "~5.0.20",
"expo-file-system": "^18.0.12",
"expo-haptics": "~14.0.1",
"expo-image": "~2.0.7",
@ -40,7 +42,7 @@
"expo-screen-orientation": "~8.0.4",
"expo-status-bar": "~2.0.1",
"expo-system-ui": "^4.0.9",
"expo-web-browser": "^14.0.2",
"expo-web-browser": "~14.0.2",
"lodash": "^4.17.21",
"react": "18.3.1",
"react-native": "0.76.9",
@ -56,10 +58,10 @@
"react-native-screens": "~4.4.0",
"react-native-svg": "^15.11.2",
"react-native-tab-view": "^4.0.10",
"react-native-url-polyfill": "^2.0.0",
"react-native-video": "^6.12.0",
"react-native-web": "~0.19.13",
"subsrt": "^1.1.1",
"expo-dev-client": "~5.0.20"
"subsrt": "^1.1.1"
},
"devDependencies": {
"@babel/core": "^7.25.2",

View file

@ -13,7 +13,7 @@ import {
Platform,
} from 'react-native';
import { useNavigation } from '@react-navigation/native';
import { makeRedirectUri, useAuthRequest, ResponseType } from 'expo-auth-session';
import { makeRedirectUri, useAuthRequest, ResponseType, Prompt, CodeChallengeMethod } from 'expo-auth-session';
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
import { traktService, TraktUser } from '../services/traktService';
import { colors } from '../styles/colors';
@ -23,12 +23,11 @@ import TraktIcon from '../../assets/rating-icons/trakt.svg';
const ANDROID_STATUSBAR_HEIGHT = StatusBar.currentHeight || 0;
// Trakt configuration (replace with your actual Client ID if different)
// Trakt configuration
const TRAKT_CLIENT_ID = 'd7271f7dd57d8aeff63e99408610091a6b1ceac3b3a541d1031a48f429b7942c';
const discovery = {
authorizationEndpoint: 'https://trakt.tv/oauth/authorize',
// Note: Trakt doesn't use a standard token endpoint for the auth code flow
// We'll handle the code exchange manually in `traktService`
tokenEndpoint: 'https://api.trakt.tv/oauth/token',
};
// For use with deep linking
@ -55,7 +54,7 @@ const TraktSettingsScreen: React.FC = () => {
const profile = await traktService.getUserProfile();
setUserProfile(profile);
} else {
setUserProfile(null); // Ensure profile is cleared if not authenticated
setUserProfile(null);
}
} catch (error) {
logger.error('[TraktSettingsScreen] Error checking auth status:', error);
@ -68,13 +67,15 @@ const TraktSettingsScreen: React.FC = () => {
checkAuthStatus();
}, [checkAuthStatus]);
// Setup expo-auth-session hook
// Setup expo-auth-session hook with PKCE
const [request, response, promptAsync] = useAuthRequest(
{
clientId: TRAKT_CLIENT_ID,
scopes: [], // Trakt doesn't use scopes for standard auth code flow
scopes: [],
redirectUri: redirectUri,
responseType: ResponseType.Code, // Ask for the authorization code
responseType: ResponseType.Code,
usePKCE: true,
codeChallengeMethod: CodeChallengeMethod.S256,
},
discovery
);
@ -84,15 +85,15 @@ const TraktSettingsScreen: React.FC = () => {
// Handle the response from the auth request
useEffect(() => {
if (response) {
setIsExchangingCode(true); // Indicate we're processing the response
if (response.type === 'success') {
setIsExchangingCode(true);
if (response.type === 'success' && request?.codeVerifier) {
const { code } = response.params;
logger.log('[TraktSettingsScreen] Auth code received:', code);
traktService.exchangeCodeForToken(code)
traktService.exchangeCodeForToken(code, request.codeVerifier)
.then(success => {
if (success) {
logger.log('[TraktSettingsScreen] Token exchange successful');
checkAuthStatus(); // Re-check auth status and fetch profile
checkAuthStatus();
} else {
logger.error('[TraktSettingsScreen] Token exchange failed');
Alert.alert('Authentication Error', 'Failed to complete authentication with Trakt.');
@ -110,12 +111,11 @@ const TraktSettingsScreen: React.FC = () => {
Alert.alert('Authentication Error', response.error?.message || 'An error occurred during authentication.');
setIsExchangingCode(false);
} else {
// Handle other response types like 'cancel', 'dismiss' if needed
logger.log('[TraktSettingsScreen] Auth response type:', response.type);
setIsExchangingCode(false);
}
}
}, [response, checkAuthStatus]);
}, [response, checkAuthStatus, request?.codeVerifier]);
const handleSignIn = () => {
promptAsync(); // Trigger the authentication flow

View file

@ -125,7 +125,7 @@ export class TraktService {
/**
* Exchange the authorization code for an access token
*/
public async exchangeCodeForToken(code: string): Promise<boolean> {
public async exchangeCodeForToken(code: string, codeVerifier: string): Promise<boolean> {
await this.ensureInitialized();
try {
@ -139,11 +139,14 @@ export class TraktService {
client_id: TRAKT_CLIENT_ID,
client_secret: TRAKT_CLIENT_SECRET,
redirect_uri: TRAKT_REDIRECT_URI,
grant_type: 'authorization_code'
grant_type: 'authorization_code',
code_verifier: codeVerifier
})
});
if (!response.ok) {
const errorBody = await response.text();
logger.error('[TraktService] Token exchange error response:', errorBody);
throw new Error(`Failed to exchange code: ${response.status}`);
}