-
Notifications
You must be signed in to change notification settings - Fork 86
Open
Description
I tried multiple ways to set up a fully client-side Auth following the Demo with no success.
I am using React with Typescript, but, the following code can be adapted to any use case. Somehow, it works very good, and, definetely better than the demo at the docs, since, it was hard to adapt to most of the use-cases. I cloned the repo, copypasted the whole thing, and then, ended up with the following code:
import React, {
ReactNode,
createContext,
useCallback,
useEffect,
useState
} from 'react'
import {
AuthorizationCodeWithPKCEStrategy,
Scopes,
SpotifyApi,
UserProfile
} from '@spotify/web-api-ts-sdk'
interface SpotifyContextType {
user: UserProfile | null
logout: () => void
handleLogoutButtonClick: () => void
handleLoginButtonClick: () => void
sdk: SpotifyApi | null
}
export const SpotifyAuthContext = createContext<SpotifyContextType>(
{} as SpotifyContextType
)
export const SpotifyProvider: React.FC<{ children: ReactNode }> = ({
children
}) => {
const [sdk, setSdk] = useState<SpotifyApi | null>(null)
const [user, setUser] = useState<UserProfile | null>(null)
const authenticate = useCallback(async () => {
const auth = new AuthorizationCodeWithPKCEStrategy(
'yourClientId',
`${window.location.origin}/yourRedirectPath`,
Scopes.userDetails // adatap do your scopes
)
const internalSdk = new SpotifyApi(auth)
setSdk(internalSdk)
try {
const { authenticated } = await internalSdk.authenticate()
if (authenticated) {
setSdk(() => internalSdk)
}
} catch (e: Error | unknown) {
const error = e as Error
if (
error &&
error.message &&
error.message.includes('No verifier found in cache')
) {
console.error(
"If you are seeing this error in a React Development Environment it's because React calls useEffect twice. Using the Spotify SDK performs a token exchange that is only valid once, so React re-rendering this component will result in a second, failed authentication. This will not impact your production applications (or anything running outside of Strict Mode - which is designed for debugging components).",
error
)
} else {
console.error(e)
}
}
}, [])
const handleLoginButtonClick = useCallback(async () => {
authenticate()
}, [authenticate])
useEffect(() => {
if (location.search.includes('code=') && sdk === null) {
authenticate()
return
}
const storageToken = JSON.parse(
localStorage.getItem(
'spotify-sdk:AuthorizationCodeWithPKCEStrategy:token'
) ?? '{}'
)
const hasToken = Boolean(storageToken?.access_token)
if (hasToken && user === null && sdk === null) {
authenticate()
return
}
}, [sdk, user, authenticate, location.search])
useEffect(() => {
if (user !== null) return
if (sdk === null) return
const fetchUser = async () => {
try {
const user = await sdk.currentUser.profile()
setUser(user)
} catch (e: Error | unknown) {}
}
fetchUser()
}, [sdk, user])
const logout = () => {
sdk?.logOut()
setUser(null)
setSdk(null)
}
const handleLogoutButtonClick = () => {
logout()
}
const value = {
user,
logout,
handleLogoutButtonClick,
handleLoginButtonClick,
sdk
}
return (
<SpotifyAuthContext.Provider value={value}>
{children}
</SpotifyAuthContext.Provider>
)
}Live demo at:
https://mettracks.com
Please let me know if this is useful for you, if you have any issue or suggestion. If I this is helpful, I'll raise a PR to update the docs with a more generic approach following this same logic
makebbekus
Metadata
Metadata
Assignees
Labels
No labels