BA
Better Auth•3w ago
sven09

RefressAcceshToken in SocialProvider (Microsoft) and NextJs 15

Hey, i am struggling to trigger the refreshAccesstoken for Microsoft. Single Sign on works fine, i got an accesstoken and an accessTokenExpiresAt and a refreshtoken. But how do i trigger to refresh the accesstoken automatically refreshed in when it is expired. I thought, this is done automatically. In need the accesstoken to be updated silently because i am calling some functionality on Azure and need it as bearer. What is the best way to trigger / monitor this? Many thanks for your help Kind Regards Sven
13 Replies
KiNFiSH
KiNFiSH•3w ago
check refreshAccessToken fn here - https://www.better-auth.com/docs/concepts/oauth
OAuth | Better Auth
How Better Auth handles OAuth
KiNFiSH
KiNFiSH•3w ago
like this -
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
refreshAccessToken: async (refreshToken) => {
// do you logic

},
},
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
refreshAccessToken: async (refreshToken) => {
// do you logic

},
},
Ping
Ping•3w ago
@bekacru the refreshAccessToken should trigger automatically when access token expires, right? 🤔
Syntarex
Syntarex•3w ago
Hi Sven! I'm curious on how you got the Microsoft AccessToken. Im trying to get it too so I can call Graph on User behalf.
sven09
sven09OP•3w ago
I have solved it and will post it tomorrow as soon as i am back to my Computer
Syntarex
Syntarex•3w ago
Ah that would be awesome. 🙂
KiNFiSH
KiNFiSH•3w ago
The user might not want to refresh the token just because it has expired. There could be scenarios where token refreshing needs to be handled on a based on some condition, but by default, it can be set to refresh upon expiry.
sven09
sven09OP•3w ago
I managed to do this all on nextjs / prisma / hono project. Here are the pieces.
export async function getValidAccessToken(userId: string, providerId: string = "microsoft") {
// Find the account for the user and provider
const account = await db.account.findFirst({
where: {
userId,
providerId,
},
});

if (!account) {
throw new Error(`No ${providerId} account found for user ${userId}`);
}

// Check if the access token is expired or will expire soon (within 5 minutes)
const isExpiredOrExpiringSoon = !account.accessTokenExpiresAt ||
account.accessTokenExpiresAt.getTime() < Date.now() + 5 * 60 * 1000;

// If the token is expired or will expire soon, refresh it
if (isExpiredOrExpiringSoon && account.refreshToken) {
try {
console.log(`Access token is expired or will expire soon. Refreshing...`);

// Get the provider configuration from auth
const providerConfig = auth.options.socialProviders?.[providerId];

if (!providerConfig || !providerConfig.refreshAccessToken) {
throw new Error(`Provider ${providerId} does not support token refreshing`);
}

// Call the refreshAccessToken function
const tokens = await providerConfig.refreshAccessToken(account.refreshToken);

// Update the account with the new tokens
await db.account.update({
where: { id: account.id },
data: {
accessToken: tokens.accessToken,
refreshToken: tokens.refreshToken,
accessTokenExpiresAt: tokens.accessTokenExpiresAt,
refreshTokenExpiresAt: tokens.refreshTokenExpiresAt,
idToken: tokens.idToken,
},
});

console.log(`Access token refreshed successfully. New expiration: ${tokens.accessTokenExpiresAt}`);

return tokens.accessToken;
} catch (error) {
console.error("Error refreshing access token:", error);
throw error;
}
}

// Return the current access token if it's still valid
return account.accessToken;
}
export async function getValidAccessToken(userId: string, providerId: string = "microsoft") {
// Find the account for the user and provider
const account = await db.account.findFirst({
where: {
userId,
providerId,
},
});

if (!account) {
throw new Error(`No ${providerId} account found for user ${userId}`);
}

// Check if the access token is expired or will expire soon (within 5 minutes)
const isExpiredOrExpiringSoon = !account.accessTokenExpiresAt ||
account.accessTokenExpiresAt.getTime() < Date.now() + 5 * 60 * 1000;

// If the token is expired or will expire soon, refresh it
if (isExpiredOrExpiringSoon && account.refreshToken) {
try {
console.log(`Access token is expired or will expire soon. Refreshing...`);

// Get the provider configuration from auth
const providerConfig = auth.options.socialProviders?.[providerId];

if (!providerConfig || !providerConfig.refreshAccessToken) {
throw new Error(`Provider ${providerId} does not support token refreshing`);
}

// Call the refreshAccessToken function
const tokens = await providerConfig.refreshAccessToken(account.refreshToken);

// Update the account with the new tokens
await db.account.update({
where: { id: account.id },
data: {
accessToken: tokens.accessToken,
refreshToken: tokens.refreshToken,
accessTokenExpiresAt: tokens.accessTokenExpiresAt,
refreshTokenExpiresAt: tokens.refreshTokenExpiresAt,
idToken: tokens.idToken,
},
});

console.log(`Access token refreshed successfully. New expiration: ${tokens.accessTokenExpiresAt}`);

return tokens.accessToken;
} catch (error) {
console.error("Error refreshing access token:", error);
throw error;
}
}

// Return the current access token if it's still valid
return account.accessToken;
}
` in your social provider i can post here anymore...
sven09
sven09OP•3w ago
sven09
sven09OP•3w ago
make sure that you have offline_access in your token config and scope
Syntarex
Syntarex•3w ago
Ah that makes sense! Thanks! 🙂
sven09
sven09OP•3w ago
5 hours of research (aka tears): if you put in user.read in the scope the token will be issued for msgraph (The reason your access token’s aud (audience) claim is set to "00000003-0000-0000-c000-000000000000" is because this GUID represents the Microsoft Graph API.) and add your api to the scope api://<your-client-id>/access_as_user now i can get an accesstoken in nextjs for an app registration and call a azure resource with the accesstoken as bearer How can it be configured?
bekacru
bekacru•3w ago
I think we should provide getAccessToken helper that refresh the token automatically, if it's expired. It shouldn't require this much work.

Did you find this page helpful?