Skip to content

Commit f16a24a

Browse files
committed
fix: no more double auth calls + race condition
1 parent 07eb4d2 commit f16a24a

File tree

2 files changed

+28
-5
lines changed

2 files changed

+28
-5
lines changed

src/components/auth/AuthScreen.jsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useEffect, useState } from "react";
1+
import React, { useEffect, useState, useRef } from "react";
22
import { useGradientState } from "../../hooks/useGradientState";
33
import { useAuth } from "../../hooks/useAuth";
44
import NocturneIcon from "../common/icons/NocturneIcon";
@@ -7,6 +7,7 @@ import QRCodeDisplay from "./QRCodeDisplay";
77
const AuthScreen = ({ onAuthSuccess }) => {
88
const [error, setError] = useState(null);
99
const [authInitialized, setAuthInitialized] = useState(false);
10+
const authAttemptedRef = useRef(false);
1011

1112
const { authData, isLoading, initAuth, pollAuthStatus, isAuthenticated } =
1213
useAuth();
@@ -23,7 +24,9 @@ const AuthScreen = ({ onAuthSuccess }) => {
2324
useEffect(() => {
2425
updateGradientColors(null, "auth");
2526

26-
if (!authInitialized && !isAuthenticated) {
27+
if (!authInitialized && !isAuthenticated && !authAttemptedRef.current) {
28+
authAttemptedRef.current = true;
29+
2730
const startAuth = async () => {
2831
try {
2932
const storedAccessToken = localStorage.getItem("spotifyAccessToken");
@@ -97,8 +100,8 @@ const AuthScreen = ({ onAuthSuccess }) => {
97100
error
98101
? error
99102
: authData === null
100-
? "Failed to generate QR code"
101-
: null
103+
? "Failed to generate QR code"
104+
: null
102105
}
103106
/>
104107
</div>

src/hooks/useAuth.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ import {
55
refreshAccessToken,
66
} from "../services/authService";
77

8+
const authInitializationState = {
9+
initializing: false,
10+
refreshing: false
11+
};
12+
813
export function useAuth() {
914
const [isAuthenticated, setIsAuthenticated] = useState(false);
1015
const [accessToken, setAccessToken] = useState(null);
@@ -16,12 +21,16 @@ export function useAuth() {
1621
const pollingIntervalRef = useRef(null);
1722
const refreshTimerRef = useRef(null);
1823
const currentDeviceCodeRef = useRef(null);
24+
const initCalledRef = useRef(false);
1925

2026
const refreshTokens = useCallback(async () => {
2127
try {
2228
const storedRefreshToken = localStorage.getItem("spotifyRefreshToken");
2329
if (!storedRefreshToken) return false;
2430

31+
if (authInitializationState.refreshing) return false;
32+
authInitializationState.refreshing = true;
33+
2534
if (pollingIntervalRef.current) {
2635
clearInterval(pollingIntervalRef.current);
2736
pollingIntervalRef.current = null;
@@ -49,14 +58,17 @@ export function useAuth() {
4958

5059
scheduleTokenRefresh(expiryDate);
5160

61+
authInitializationState.refreshing = false;
5262
return true;
5363
}
64+
authInitializationState.refreshing = false;
5465
return false;
5566
} catch (err) {
5667
console.error("Token refresh failed:", err);
5768
if (err.message?.includes("invalid_grant")) {
5869
logout();
5970
}
71+
authInitializationState.refreshing = false;
6072
return false;
6173
}
6274
}, []);
@@ -112,6 +124,9 @@ export function useAuth() {
112124

113125
useEffect(() => {
114126
const initAuthState = async () => {
127+
if (initCalledRef.current) return;
128+
initCalledRef.current = true;
129+
115130
const storedAccessToken = localStorage.getItem("spotifyAccessToken");
116131
const storedRefreshToken = localStorage.getItem("spotifyRefreshToken");
117132

@@ -141,6 +156,9 @@ export function useAuth() {
141156

142157
const initAuth = useCallback(async () => {
143158
try {
159+
if (authInitializationState.initializing) return null;
160+
authInitializationState.initializing = true;
161+
144162
if (pollingIntervalRef.current) {
145163
clearInterval(pollingIntervalRef.current);
146164
pollingIntervalRef.current = null;
@@ -153,10 +171,12 @@ export function useAuth() {
153171
const auth = await oauthAuthorize();
154172
setAuthData(auth);
155173

174+
authInitializationState.initializing = false;
156175
return auth;
157176
} catch (error) {
158177
setError("Failed to initialize authentication");
159178
console.error("Auth initialization failed:", error);
179+
authInitializationState.initializing = false;
160180
return null;
161181
} finally {
162182
setIsLoading(false);
@@ -165,7 +185,7 @@ export function useAuth() {
165185

166186
const pollAuthStatus = useCallback(
167187
(deviceCode) => {
168-
if (isAuthenticated) return () => {};
188+
if (isAuthenticated) return () => { };
169189

170190
if (pollingIntervalRef.current) {
171191
clearInterval(pollingIntervalRef.current);

0 commit comments

Comments
 (0)