-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: next-auth/expo
#5240
base: main
Are you sure you want to change the base?
feat: next-auth/expo
#5240
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
Balázs seem to approve of this idea:
Excited to use this! |
Very excited to see this integration land @intagaming! 🙌 |
A very important missing piece in the Expo ecosystem 👏👏 |
I'm working on an app, Next.js & Expo monorepo, which is currently using the next-auth built from the PR branch. Anything new and I'll post it here. Meanwhile if anyone's also solving issues please collaborate ;) So today I digged into the Email Provider. This could probably be done on Expo as well, but I think it would not be a good UX. We are relying on the user clicking a link in the email. What if the email never came? How to link it to the Expo app for token submission? What happens if we click the same link on desktop? It seems like there's many problems to be solved if we go this route, and even then it might not be pleasent to use. I also feel like the Credentials Provider is not the route anyone should invest in. Passwords are cumbersome. Though it might be convenient, it should be limited as much as possible, starting today. Since I don't want to use an app that requires password, I won't do that to my users. I prefer OAuth. (Though Yubikey seems interesting, but don't know how it ended up in Password land, it seems unrelated to each other.) So there's that, even though I'm using Google or GitHub with a password myself, I think it's okay for the time being as long as I don't have to create another password on a lesser-known site/app. Anyone interested, please chime in, but I'll probably leave these parts up to the interested ones. As for the Expo Authentication initiate functions, I'm thinking of a folder at
import { nativeProviders } from "@acme/constants";
import * as AuthSession from "expo-auth-session";
import { discovery as googleDiscovery } from "expo-auth-session/providers/google";
import { getSignInInfo, SigninResult } from "next-auth/expo";
import { Alert } from "react-native";
export const signinGoogle = async (): Promise<SigninResult | null> => {
const redirectUri = AuthSession.makeRedirectUri({ useProxy: true });
const provider = nativeProviders.google; // providerId
const signinInfo = await getSignInInfo({
provider,
proxyRedirectUri: redirectUri,
});
if (!signinInfo) {
Alert.alert("Error", "Couldn't get sign in info from server");
return null;
}
const { state, codeChallenge, stateEncrypted, codeVerifier, clientId } =
signinInfo;
// This corresponds to useLoadedAuthRequest
const request = new AuthSession.AuthRequest({
clientId,
redirectUri,
scopes: [
"openid",
"https://www.googleapis.com/auth/userinfo.profile",
"https://www.googleapis.com/auth/userinfo.email",
],
});
request.state = state;
request.codeChallenge = codeChallenge;
request.codeVerifier = codeVerifier;
await request.makeAuthUrlAsync(googleDiscovery);
// useAuthRequestResult
const result = await request.promptAsync(googleDiscovery, { useProxy: true });
return {
result,
state,
stateEncrypted,
codeVerifier,
provider,
};
}; Discord (old code, not using the library but similar concept)
export const signinDiscord = async () => {
const proxyRedirectUri = AuthSession.makeRedirectUri({ useProxy: true }); // https://auth.expo.io
// This corresponds to useLoadedAuthRequest
const request = new AuthSession.AuthRequest({
clientId: Constants.manifest?.extra?.discordId ?? "",
scopes: ["identify", "email"],
redirectUri: proxyRedirectUri,
usePKCE: false,
});
const discovery = {
authorizationEndpoint: "https://discord.com/api/oauth2/authorize",
tokenEndpoint: "https://discord.com/api/oauth2/token",
revocationEndpoint: "https://discord.com/api/oauth2/token/revoke",
};
const provider = nativeProviders.discord;
const {
state,
// codeChallenge,
csrfTokenCookie,
stateEncrypted,
// codeVerifier,
} = await trpcClient.query("auth.signIn", {
provider,
proxyRedirectUri,
usePKCE: false,
});
request.state = state;
// request.codeChallenge = codeChallenge;
await request.makeAuthUrlAsync(discovery);
// useAuthRequestResult
const result = await request.promptAsync(discovery, { useProxy: true });
return {
result,
state,
csrfTokenCookie,
stateEncrypted,
// codeVerifier,
proxyRedirectUri,
provider,
};
}; |
There seems to be a possibility of using a combination of next-auth/expo and expo-apple-authentication to achieve the native iOS "Sign In With Apple" widget flow. I've tested successful signin with most of the setup similar to what you've described in this PR @intagaming, except for replacing the calls to next-auth/expo's One downside I see is that this process doesn't seem like it can work when testing with Expo Go. The auth code produced by Apple's sign in widget is tied to bundle identifier of the app that launches the sign in widget, and the Client ID/Client Secret used in the next-auth backend is tied to the App ID setup configured for your app in Apple's Certificates, Identifiers, and Profiles portal; if they don't match then Apple's token auth endpoint will return an error. Running your app in Expo Go produces an auth code from Apple's signin widget using Expo Go app's bundle identifier (host.exp.Exponent), so when the next-auth backend calls apple's auth endpoint to verify it using the Client ID/Secret generated from your App ID, it fails:
|
any news on this PR? |
Actually yes. I aborted the intent of doing a native app at the moment, so this might take another very long time unless someone steps in and continue the work. I could provide assistant to anyone willing to. It's unfortunate that I don't get the time to work on this more. I'm still looking for a self-hosted Authorization Server. If someone actually has the demand to get this working, please take matter into your hand - it's very easy to manoeuvre the code. Even I can do it with limited knowledge about everything. I'm now just like the people that landed here - I'm watching the progress being made by the community. If it actually has demand, it should receive the work it deserves. |
19c6807
to
3be7bb7
Compare
@tmlamb I've been studying your implementation and playing with it on iOS. Really smooth integration with Apple, for Google Auth it redirects to expo.io which is probably a bit alarming it some. Honestly I'm just starting to grok what is happening with this implementation. Great job. Going to try and implement something similar, it seems you've hit the holy grail of code sharing. Thank you for making this project public. |
I'm glad you found it helpful @nickreese. I agree that getting rid of the expo proxy in the google flow is important. The ease at which it's working with the Apple flow without a proxy makes me hopeful, and I do plan on giving it a try when I have time. |
Been taking a stab at this from time to time lately and got it working for t3-turbo for those interested: t3-oss/create-t3-turbo#133 Will take a look at implementing into the new authjs monorepo structure if i have the time - probably best to wait for the |
Hey are there any updates on this or other next-auth expo integration efforts? |
9784f29
to
37bb6eb
Compare
fa96b45
to
65aa467
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very excited to use this. Brilliant!
LGTM!
Need this so much! What needs to be done in order for this to be merged eventually? |
any doc for this ? |
☕️ Reasoning
I attempted to create the
next-auth/expo
module that supports using NextAuth in Expo, with an external Next.js server acting as the NextAuth Authorization Server.The hope is that developers who want to have a Next.js + Expo monorepo could use NextAuth as its common authentication method.
In the general scheme of things, maybe NextAuth could become a "self-hosted Authorization Server". Currently there's no way for NextAuth to be used on Expo, so it's one step closer towards the common goal.
🧢 Checklist
🎫 Affected issues
There might be issues that's related to this, I just didn't go scavenge to get them here.
💡 Explanation
Here's how things work currently. The login flow looks like this:
signIn()
function fromnext-auth/expo
. It takes a function that initiate the Expo Authentication flow. Inside thissignIn()
function, it invokes the argument function with the hope of obtaining the authentication result so that it can send them to the/api/auth/callback
to get thesessionToken
.getSignInInfo()
function innext-auth/expo
to get the OAuth information required to initiate the Expo Auth Flow. ThegetSignInInfo()
will make aPOST
call to/api/auth/proxy
with theaction
ofsignin
in the body, indicating a proxy request to/api/auth/signin
. The proxy makes aNextAuthHandler()
call simulating aPOST
to/api/auth/signin
. It then gets whatever it needs in the response and return the result to the Expo app.signIn()
function innext-auth/expo
. Assuming everything went ok, it will now make a proxy request to the/api/auth/callback
with the auth information obtained and hope that asessionToken
comes back. (By "making a proxy request" I mean making aPOST
request to/api/auth/proxy
, so keep that in mind.)sessionToken
comes back, thesignIn()
function will store the token in Expo'sSecureStore
, then doawait __NEXTAUTH._getSession({ event: "storage" })
so that theSessionProvider
knows to go and fetch the session.getSession()
function innext-auth/expo
will go and fetch the session. It does so by making a proxy request to/api/auth/session
(via/api/auth/proxy
, remember). Every request to/api/auth/proxy
will include thesessionToken
in the body. In the proxy handler, it will convert this body parameter into a cookie before simulating the request to the destination endpoint, like so:The login flow is now complete.
✔️ Todo
If by any chance this caught the interest of somebody, I would like to ask for some help with these following problems:
next-auth
package. I have little experience with this so I have no idea what's proper to put in. It seems like the React peer dependency is unhappy right now (it is asking for React 18.0.0 and 18.2.0 something something which I don't understand.)next-auth/expo
is straight up a derivative ofnext-auth/react
, with some modifications. There are still residues, missing cases, and anything new is solely made up by me. So I need some help in there to polish up. (Side note:fetchData
is a mod of thefetchData
fromnext-auth/client/_utils.ts
.)There might be a few more. If I realize something I'll post in the comment & update it here.
📌 Resources
next-auth/expo
module. Just use this as a reference.The Next project I use to develop this (it's a create-t3-app)The Expo project I use to develop this (fresh Expo 46 app)github-expo
provider is set up like so:Google provider setup, different from GitHub.
Here's how I currently set up the Expo Auth flow on the Expo app: