Skip to content
Migrating from NextAuth.js v4? Read our migration guide.

Azure DevOps Provider

⚠️

Deprecated - While still available, Microsoft is no longer supporting Azure DevOps OAuth and recommends using Microsoft Entra ID instead.

Resources

Setup

Callback URL

https://example.com/api/auth/callback/azure-devops

Environment variables

In .env.local create the following entries:

AZURE_DEVOPS_APP_ID=<copy App ID value here>
AZURE_DEVOPS_CLIENT_SECRET=<copy generated client secret value here>

Register application

https://app.vsaex.visualstudio.com/app/register

Provide the required details:

  1. Company name
  2. Application name
  3. Application website
  4. Authorization callback URL
    • https://example.com/api/auth/callback/azure-devops for production
    • https://localhost/api/auth/callback/azure-devops for development
  5. Authorized scopes
    • Required minimum is User profile (read)

Click ‘Create Application’

⚠️
  • You are required to use HTTPS even for the localhost

  • You will have to delete and create a new application to change the scopes later

The following data is relevant for the next step:

  • App ID
  • Client Secret (after clicking the ‘Show’ button, ignore App Secret entry above it)
  • Authorized Scopes

Configuration

/auth.ts
import NextAuth from "next-auth"
import AzureDevOps from "next-auth/providers/azure-devops"
 
export const { handlers, auth, signIn, signOut } = NextAuth({
  providers: [
    AzureDevOps({
      clientId: AUTH_AZURE_DEVOPS_APP_ID,
      clientSecret: AUTH_AZURE_DEVOPS_CLIENT_SECRET,
    }),
  ],
})

Refresh token rotation

Use the main guide as your starting point with the following considerations:

./auth.ts
export const { signIn, signOut, auth } = NextAuth({
  callbacks: {
    async jwt({ token, user, account }) {
      // The token has an absolute expiration time
      const accessTokenExpires = account.expires_at * 1000
    },
  },
})
 
async function refreshAccessToken(token) {
  const response = await fetch(
    "https://app.vssps.visualstudio.com/oauth2/token",
    {
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
      method: "POST",
      body: new URLSearchParams({
        client_assertion_type:
          "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
        client_assertion: AZURE_DEVOPS_CLIENT_SECRET,
        grant_type: "refresh_token",
        assertion: token.refreshToken,
        redirect_uri:
          process.env.NEXTAUTH_URL + "/api/auth/callback/azure-devops",
      }),
    }
  )
 
  // The refreshed token comes with a relative expiration time
  const accessTokenExpires = Date.now() + newToken.expires_in * 1000
}
Auth.js © Balázs Orbán and Team - 2024