在Next.js应用程序中使用NextAuth.js安全地处理Azure AD访问令牌

mhd8tkvw  于 2023-08-07  发布在  其他
关注(0)|答案(1)|浏览(150)

我正在开发一个Next.js应用程序,我正在使用NextAuth.js进行Azure AD的身份验证。成功验证后,我将获得访问令牌,稍后将使用该令牌调用Microsoft Graph API。
然而,我注意到当使用React Dev Tools查看时,访问令牌在React上下文中是可见的。下面是我如何处理身份验证的一个简化示例:

import NextAuth, { NextAuthOptions } from 'next-auth'
import AzureADProvider from 'next-auth/providers/azure-ad'

export const authOptions: NextAuthOptions = {
  // Configure one or more authentication providers
  providers: [
    AzureADProvider({
      clientId: process.env.AZURE_AD_CLIENT_ID!,
      clientSecret: process.env.AZURE_AD_CLIENT_SECRET!,
      tenantId: process.env.AZURE_AD_TENANT_ID!,
      authorization: { params: { scope: 'openid profile user.Read email' },
    url: `https://login.microsoftonline.com/${process.env.AZURE_AD_TENANT_ID!}/oauth2/v2.0/authorize`
    },
    token: { params
      : { scope: 'openid profile user.Read email' },
    url: `https://login.microsoftonline.com/${process.env.AZURE_AD_TENANT_ID!}/oauth2/v2.0/token`
    },
    })
  ],
  callbacks: {
    async jwt({ token, user, account, profile }) {
      // Persist the OAuth access_token to the token right after signin
      console.log("JWT token", token);

      if (account) {
        token.accessToken = account.access_token;
      }
      return token;
    },

    async session({ session, token, user }) {
      // Send properties to the client, like an access_token and user id from a provider.
      console.log("Session token", token);
      if (token) {
        session.accessToken = token.accessToken;
      }
      return session
    }
    
  },

  
  
}

export default NextAuth(authOptions)

字符串
然后,在我的组件中,我像这样访问这个令牌:

import { useSession } from 'next-auth/react'

export default function Component() {
  const { data: session } = useSession();
  console.log("accesstoken", session.accessToken);

  // I then use session.accessToken to make authenticated calls to the Microsoft Graph API.
}


虽然我知道令牌是短暂的,并且特定于用户和会话,但我仍然担心它在React上下文中的可见性,因为它包含敏感信息。
这种方法是否安全,或者是否有更好的方法在Next.js应用程序中使用NextAuth.js在客户端处理访问令牌?我是否应该关注访问令牌在React上下文中的可见性?如果这是一个安全风险,我可以使用哪些替代策略来安全地存储和管理访问令牌?提前感谢您的指导。
我已经考虑过使用next-auth适配器将用户数据扔到数据库中,当我需要accessToken时,从数据库中获取它,但这似乎也不是一个很好的选择,你怎么看?

dzhpxtsq

dzhpxtsq1#

我认为,当您将JWT添加到session时,您有一个非常合理的担忧,即在客户端暴露JWT。当您这样做时,您就否定了NextAuth在将其存储到客户端之前对您添加到JWT的详细信息进行加密的工作。
也许你可以从你的会话中删除访问令牌,依靠你的服务器来处理对MS Graph的认证请求?再次探索使用适配器也可能是很好的,因为NextAuth将在DB中存储任何自定义会话数据,而无需执行任何额外的工作。
我不知道你使用的是哪个版本的Next,所以我假设这个例子是Next@13。
首先更新您的NextAuth配置以从会话中删除访问令牌。还可以添加您选择的适配器。

import NextAuth, { NextAuthOptions } from 'next-auth';
import AzureADProvider from 'next-auth/providers/azure-ad';
// Use whichever adapter suits you. This is just an example.
import { DynamoDBAdapter } from '@auth/dynamodb-adapter'

const client = /* your db client */

export const authOptions: NextAuthOptions = {
  // Configure one or more authentication providers
  providers: [
    AzureADProvider({
      /* your config here */
    }),
  ],
  // Use whichever adapter suits you. This is just an example.
  adapter: DynamoDBAdapter(
    client
  ),
  callbacks: {
    async jwt({ token, user, account, profile }) {
      // Persist the OAuth access_token to the token right after signin
      console.log("JWT token", token);

      if (account) {
        token.accessToken = account.access_token;
      }

      return token;
    },
    async session({ session, token, user }) {
      if (token) {
        // Instead of returning the access token, maybe you could only return
        // the properties you want to see on the FE that aren't
        // considered sensitive data. e.g.
        session.user.roles = token.roles;
      }

      return session;
    },
  },
}

export default NextAuth(authOptions)

字符串
接下来,让我们创建一个查询MS Graph的API路由

// app/api/msgraph/route.ts

import { NextResponse } from 'next/server'
import { redirect } from 'next/navigation'
import { getToken } from 'next-auth/jwt'

export async function GET(req: Request) {
  const token = await getToken({ req })

  // If the user isn't signed in, redirect
  if (!token) return redirect('/signin')

  // Modify the fetch request to match your needs
  const res = await fetch('https://graph.microsoft.com/{version}/{resource}?{query-parameters}', {
    // Add the  access token to your request
    headers: { Authorization: `Bearer ${token?.accessToken}` },
  })

  const data = await res.json()
 
  return NextResponse.json({ data })
}


然后你可以像这样调用这个API端点:

import useSWR from 'swr'

export default function Component() {
  const { data, error, isLoading } = useSWR('/api/msgraph', fetcher)
  
  if (error) return <div>❌</div>
  if (isLoading) return <div>😵‍💫</div>
  
  console.log('ms graph response', data)
  return <div>{data.someProp}</div>
}


代码可能有bug,因为我只是在写这个,但我认为它会满足您的需要,并减轻您对在应用程序中共享访问令牌的担忧。
与您发布的代码相关,您可能需要考虑以更安全的方式获取以下详细信息:

clientId: process.env.AZURE_AD_CLIENT_ID!,
clientSecret: process.env.AZURE_AD_CLIENT_SECRET!,
tenantId: process.env.AZURE_AD_TENANT_ID!


如果您将它们存储为Lambda,Azure Function或EC2示例等的环境变量,则会出现安全问题。
希望这个答案有帮助!

相关问题