Couldn't figure out how to pass data between callbacks #1491
-
Hi, I want to implement authentication with linkedin with a custom api token. I did the nextauth + custom backend tutorial. This is it macro steps:
The first version doesn't work for me so I modified it with all the advise I could find on the repos. Finally it still doesn't work properly, so I ask you what I am doing wrong. Cinematic :
Behaviour Code from : const providers = [
Providers.LinkedIn({
clientId: process.env.LINKEDIN_CLIENT_ID,
clientSecret: process.env.LINKEDIN_CLIENT_SECRET
})
];
const callbacks = {};
callbacks.signIn = async function signIn(user, account, profile) {
if (account.provider === "linkedin") {
let emailLinkedin = "";
try {
const emailRes = await axios.get(
'https://api.linkedin.com/v2/emailAddress?q=mambers&projection=(elements*(handle~))',
{
headers: {
Authorization: `Bearer ${account.accessToken}`
}
}
);
console.log(emailRes.data);
emailLinkedin = emailRes ? await emailRes["handle~"].emailAddress : "";
} catch (error) {
console.log(error.response.data);
}
let imageLinkedin = "";
try {
const imgRes = await axios.get(
"https://api.linkedin.com/v2/me?projection=(id,firstName,lastName,profilePicture(displayImage~:playableStreams))",
{
headers: {
Authorization: `Bearer ${account.accessToken}`
}
}
);
imageLinkedin = await imgRes.data.profilePicture["displayImage~"]
.elements[0].identifiers[0].identifier;
} catch (error) {
console.log(error.response.data);
}
const linkedinUser = {
email: emailLinkedin,
image: imageLinkedin,
id: user.id,
localizedLastName: profile.localizedLastName,
localizedFirstName: profile.localizedFirstName,
name: user.name
};
//retrieve the token from the API
axios
.post(
`${process.env.NEXT_PUBLIC_API2}/mongodb/signin`,
{ user: linkedinUser },
{
headers: {
"Content-Type": "application/json"
}
}
)
.then(res => {
user.accessToken = res.data.authToken;
console.log(user.accessToken);
return Promise.resolve(true);
})
.catch(error => {
console.log(error);
return Promise.resolve(true);
});
}
};
callbacks.jwt = async function jwt(token, user) {
if (user && user.accessToken) {
token = { accessToken: user.accessToken };
}
return Promise.resolve(token);
};
callbacks.session = async function session(session, token) {
if (user && user.accessToken) {
session.accessToken = user.accessToken;
axios
.get('${process.env.NEXT_PUBLIC_API2}/mongodb/user', {
headers: {
authorization: `Bearer ${session.accessToken}`,
"Content-Type": "application/json"
}
})
.then(res => {
session.user = res.data;
})
.catch(error => {
console.log(error);
});
}
return Promise.resolve(await session);
};
const options = {
debug: true,
providers,
callbacks,
pages: {
signIn: "/fr/signin"
}
};
export default (req, res) => NextAuth(req, res, options); I check all API routes. They are woorking great. |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 4 replies
-
So I feel you have way overengineered what you were trying to achieve, here is my suggestion (Please note I haven't tested/run the code anywhere, there might be some small issues, but the main ideas remain the same). I'll try to explain everything below. export default NextAuth({
debug: true,
providers: [
Providers.LinkedIn({
clientId: process.env.LINKEDIN_CLIENT_ID,
clientSecret: process.env.LINKEDIN_CLIENT_SECRET,
async profile(profile, tokens) {
return {
email: await getEmail(tokens.accessToken),
image: await getImage(tokens.accessToken),
id: profile.id,
localizedLastName: profile.localizedLastName,
localizedFirstName: profile.localizedFirstName,
name: profile.name
}
}
})
],
callbacks: {
async signIn(profile, account) {
try {
// Create user in the database
const res = await fetch(`${process.env.NEXT_PUBLIC_API2}/mongodb/signin`, {
headers: { "Content-Type": "application/json", Authorization: `Bearer ${account.accessToken}` },
method: "POST",
body: JSON.stringify({ user: profile })
})
const data = await res.json()
if (res.ok) return true
throw data
} catch (error) {
console.error("Could not create user in database", error)
return false
}
},
async jwt(token, profile, account) {
if (profile && account) {
token.accessToken = account.accessToken
token.user = profile
}
return token
},
async session(session, token) {
session.accessToken = token.accessToken;
session.user = token.user
return session
}
},
pages: {
signIn: "/fr/signin"
}
})
async function getEmail(accessToken) {
try {
const res = await fetch(
'https://api.linkedin.com/v2/emailAddress?q=mambers&projection=(elements*(handle~))',
{ headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json" } }
)
const data = await res.json()
return data["handle~"].emailAddress ?? ""
} catch (error) {
console.error("Failed to fetch email", error)
// We return an empty string in case of a failure, so the login flow can continue. If it is a show-stopper for the sake of the app, just throw the error further up
return ""
}
}
async function getImage(accessToken) {
try {
const res = await fetch(
"https://api.linkedin.com/v2/me?projection=(profilePicture(displayImage~:playableStreams))",
{ headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json" } }
)
const data = await res.json()
return data.profilePicture["displayImage~"].elements[0].identifiers[0].identifier;
} catch (error) {
console.error("Failed to fetch image", error)
// We return an empty string in case of a failure, so the login flow can continue. If it is a show-stopper for the sake of the app, just throw the error further up
return ""
}
} In no particular order:
I tried to link to the relevant documentation where I could, please study it carefully! If something is not clear in the docs, please open an issue, so we can explain it better! |
Beta Was this translation helpful? Give feedback.
So I feel you have way overengineered what you were trying to achieve, here is my suggestion (Please note I haven't tested/run the code anywhere, there might be some small issues, but the main ideas remain the same). I'll try to explain everything below.