-
Notifications
You must be signed in to change notification settings - Fork 298
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(clerk-expo): Introduce SAML support (#4880)
Co-authored-by: Nicolas Lopes <[email protected]>
- Loading branch information
1 parent
a26cf0f
commit bd96d6c
Showing
4 changed files
with
110 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
--- | ||
'@clerk/clerk-expo': minor | ||
--- | ||
|
||
Introduce support for SSO with SAML | ||
|
||
- Introduce `useSSO` hook to support a wider range of SSO flow types | ||
- Deprecate `useOAuth` in favor of new `useSSO` hook |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import { useSignIn, useSignUp } from '@clerk/clerk-react'; | ||
import type { EnterpriseSSOStrategy, OAuthStrategy, SetActive, SignInResource, SignUpResource } from '@clerk/types'; | ||
import * as AuthSession from 'expo-auth-session'; | ||
import * as WebBrowser from 'expo-web-browser'; | ||
|
||
import { errorThrower } from '../utils/errors'; | ||
|
||
export type StartSSOFlowParams = { | ||
unsafeMetadata?: SignUpUnsafeMetadata; | ||
} & ( | ||
| { | ||
strategy: OAuthStrategy; | ||
} | ||
| { | ||
strategy: EnterpriseSSOStrategy; | ||
identifier: string; | ||
} | ||
); | ||
|
||
export type StartSSOFlowReturnType = { | ||
createdSessionId: string | null; | ||
authSessionResult?: WebBrowser.WebBrowserAuthSessionResult; | ||
setActive?: SetActive; | ||
signIn?: SignInResource; | ||
signUp?: SignUpResource; | ||
}; | ||
|
||
export function useSSO() { | ||
const { signIn, setActive, isLoaded: isSignInLoaded } = useSignIn(); | ||
const { signUp, isLoaded: isSignUpLoaded } = useSignUp(); | ||
|
||
async function startSSOFlow(startSSOFlowParams: StartSSOFlowParams): Promise<StartSSOFlowReturnType> { | ||
if (!isSignInLoaded || !isSignUpLoaded) { | ||
return { | ||
createdSessionId: null, | ||
signIn, | ||
signUp, | ||
setActive, | ||
}; | ||
} | ||
|
||
const { strategy, unsafeMetadata } = startSSOFlowParams ?? {}; | ||
|
||
/** | ||
* Creates a redirect URL based on the application platform | ||
* It must be whitelisted, either via Clerk Dashboard, or BAPI, in order | ||
* to include the `rotating_token_nonce` on SSO callback | ||
* @ref https://clerk.com/docs/reference/backend-api/tag/Redirect-URLs#operation/CreateRedirectURL | ||
*/ | ||
const redirectUrl = AuthSession.makeRedirectUri({ | ||
path: 'sso-callback', | ||
}); | ||
|
||
await signIn.create({ | ||
strategy, | ||
redirectUrl, | ||
...(startSSOFlowParams.strategy === 'enterprise_sso' ? { identifier: startSSOFlowParams.identifier } : {}), | ||
}); | ||
|
||
const { externalVerificationRedirectURL } = signIn.firstFactorVerification; | ||
if (!externalVerificationRedirectURL) { | ||
return errorThrower.throw('Missing external verification redirect URL for SSO flow'); | ||
} | ||
|
||
const authSessionResult = await WebBrowser.openAuthSessionAsync(externalVerificationRedirectURL.toString()); | ||
if (authSessionResult.type !== 'success' || !authSessionResult.url) { | ||
return { | ||
createdSessionId: null, | ||
setActive, | ||
signIn, | ||
signUp, | ||
}; | ||
} | ||
|
||
const params = new URL(authSessionResult.url).searchParams; | ||
const rotatingTokenNonce = params.get('rotating_token_nonce') ?? ''; | ||
await signIn.reload({ rotatingTokenNonce }); | ||
|
||
const userNeedsToBeCreated = signIn.firstFactorVerification.status === 'transferable'; | ||
if (userNeedsToBeCreated) { | ||
await signUp.create({ | ||
transfer: true, | ||
unsafeMetadata, | ||
}); | ||
} | ||
|
||
return { | ||
createdSessionId: signUp.createdSessionId ?? signIn.createdSessionId, | ||
setActive, | ||
signIn, | ||
signUp, | ||
}; | ||
} | ||
|
||
return { | ||
startSSOFlow, | ||
}; | ||
} |