feat(providers): return `user` object in Profile of Apple provider
☕️ Reasoning
In Apple callback, there's user object but it's not able to retrieve it using Apple provider
However, this user object only serve for initial request, subsequent request will not get this from Apple
🧢 Checklist
- [ ] Documentation
- [X] Tests
- [X] Ready to be merged
🎫 Affected issues
Not found issue related yet
📌 Resources
- Contributing guidelines
- Code of conduct
- Contributing to Open Source
The latest updates on your projects. Learn more about Vercel for Git ↗︎
1 Ignored Deployment
| Name | Status | Preview | Updated |
|---|---|---|---|
| next-auth | ⬜️ Ignored (Inspect) | May 18, 2022 at 11:30AM (UTC) |
I'm very hesitant to add provider specific code to the core.
A workaround is using advanced initialization https://next-auth.js.org/configuration/initialization#advanced-initialization
I'm very hesitant to add provider specific code to the core.
A workaround is using advanced initialization next-auth.js.org/configuration/initialization#advanced-initialization
Thanks for your reference
I find that advanced initialization could be workaround for this case but it will lack of generic handler in callbacks: { jwt, session } which share the same flow of other providers.
Do you think is there any other way to achieve this without putting specific code of a provider? Like another option in Apple provider somehow could take & process the callback. It would be great if we have this user object in OAuthProfile.
This was the case before, but we moved away from it on purpose. Our core should not care about specific providers if they implement the spec correctly. This is unfortunately on Apple. :shrug:
What I am willing to do is to pass the req here (or rather a subset to begin with, like {body, query}):
https://github.com/nextauthjs/next-auth/blob/ba6d7df3bf38bdcc5b342229babf46e45be2523e/packages/next-auth/src/core/lib/oauth/callback.ts#L125-L132
So a custom userinfo.request method could be added to Apple's configuration.
Thanks for your suggestion!
Regardless of using userinfo.request, isn't it will not execute else if (provider.idToken) which is Apple in this case?
https://github.com/nextauthjs/next-auth/blob/ba6d7df3bf38bdcc5b342229babf46e45be2523e/packages/next-auth/src/core/lib/oauth/callback.ts#L125-L133
https://github.com/nextauthjs/next-auth/blob/ba6d7df3bf38bdcc5b342229babf46e45be2523e/packages/next-auth/src/providers/apple.ts#L102-L110
Sorry, I'm confusing to the hint.
@vinhnguyen1211 Hi, if userinfo.request is defined, it will take priority over the else if (provider.idToken) block
So I think what @balazsorban44 is suggesting is something like:
if (provider.userinfo?.request) {
// @ts-expect-error
profile = await provider.userinfo.request({
provider,
tokens,
client,
requestInfo: { body, query },
})
} else if (provider.idToken) {
And in apple.ts you could define the method like:
userinfo: {
request({ provider, tokens, requestInfo: { body } }) {
const profile = tokens.claims()
if (body?.user) {
try {
profile.user = typeof body.user === 'string' ? JSON.parse(body.user) : body.user
return profile
} catch (error) {
profile.user = body.user
logger.debug("ERR_PARSING_BODY_USER_OBJECT_APPLE", {
error: error as Error,
providerId: provider.id,
})
}
}
},
},
Thanks @ThangHuuVu that solved it for me!
I just added an else to the if statement for when users log in a second time. On second login user is omitted from body.
userinfo: {
request({ provider, tokens, requestInfo: { body } }) {
const profile = tokens.claims()
if (body?.user) {
try {
profile.user = typeof body.user === 'string' ? JSON.parse(body.user) : body.user
return profile
} catch (error) {
profile.user = body.user
logger.debug("ERR_PARSING_BODY_USER_OBJECT_APPLE", {
error: error as Error,
providerId: provider.id,
})
}
} else {
return profile
}
},
},
Hi @Digital-E, Could you please share more details on how you fixed it? Did you make changes as suggested, in advanced initialisation? Can you please share the code you added?
export default async function auth(req: NextApiRequest, res: NextApiResponse) {
// What did you add here, if anything?
return await NextAuth(req, res, {
...
// I recon the above snippet was added within the apple provider config object?
})
Appreciate and thanks in advance. :+1:
Hello @balazsorban44,
Do you think you could add the requestInfo: { body, query }, parameter in the request so we can resolve this issue (maybe fix directly on the apple provider).
We use nextauth to register/login in our mobile apps and we can't publish anymore because Apple does not validate our latest update since we can't fill the user name
Until this PR is merged i solved it by doing this:
src/app/api/auth/[...nextauth]/route.ts
const authOptionsFunc = (userObject) => ({
...,
providers: [
AppleProvider({
clientId: APPLE_ID,
clientSecret: APPLE_SECRET,
profile(profile) {
if (userObject) {
profile.name = `${userObject.name.firstName} ${userObject.name.lastName}`;
}
return {
id: profile.sub,
name: profile.name,
email: profile.email,
image: null,
};
},
}),
],
});
export const authOptions = authOptionsFunc();
async function auth(req: NextApiRequest, res: NextApiResponse) {
let userObject;
if (req?.url?.includes("callback/apple") && req?.method === "POST") {
const formData = await req.clone().formData();
const user = formData.get("user");
userObject = JSON.parse(user);
}
return await NextAuth(req, res, authOptionsFunc(userObject));
}
export { auth as GET, auth as POST };
Closing this PR, there are workarounds in the user land and we don't want to add provider-specific handling in the core, as @balazsorban44 mentioned in https://github.com/nextauthjs/next-auth/pull/4579#issuecomment-1131473311