社会化登录

未匹配的标注

除了标准身份验证,AdonisJS 还附带了一个包,可帮助你使用 GoogleTwitterGitHub 等 OAuth 辅助程序实现社交身份验证。

# 主题: Install
npm i @adonisjs/ally
# 主题: Configure
node ace configure @adonisjs/ally

# CREATE: contracts/ally.ts
# CREATE: config/ally.ts
# UPDATE: .env,.env.example
# UPDATE: tsconfig.json { types += "@adonisjs/ally" }
# UPDATE: .adonisrc.json { providers += "@adonisjs/ally" }
// 主题: 验证环境变量
/**
 * 确保验证所需的环境变量
 * 由社交驱动程序配置
 *
 * 以下是针对 Google 辅助器验证环境变量的示例
 */

export default Env.rules({
  // 其他验证规则
  GOOGLE_CLIENT_ID: Env.schema.string(),
  GOOGLE_CLIENT_SECRET: Env.schema.string(),
})

配置

社交辅助器的配置存储在 config/ally.ts 文件中。你可以使用相同或不同的底层驱动程序定义一个或多个辅助程序。

const allyConfig: AllyConfig = {
  github: {
    driver: 'github',
    clientId: Env.get('GITHUB_CLIENT_ID'),
    clientSecret: Env.get('GITHUB_CLIENT_SECRET'),
    callbackUrl: 'http://localhost:3333/github',
  },
  twitter: {
    driver: 'twitter',
    clientId: Env.get('TWITTER_CLIENT_ID'),
    clientSecret: Env.get('TWITTER_CLIENT_SECRET'),
    callbackUrl: 'http://localhost:3333/twitter',
  },
}

export default allyConfig

driver

要使用的驱动程序的名称。它必须始终是以下可用驱动程序之一。

  • google
  • twitter
  • github
  • discord
  • facebook
  • linkedin
  • spotify

clientId

OAuth 提供者的客户端 ID,你必须将其安全地保存在环境变量中。


clientSecret

OAuth 提供者的客户端密码,你必须将其安全地保存在环境变量中。


callbackUrl

用于处理来自 OAuth 提供程序的后重定向响应的回调 URL。你必须向 OAuth 提供程序注册相同的 URL。


配置新的辅助器

你还可以在初始设置后配置新的辅助器。第一步是在 SocialProviders 接口下的 contracts/ally.ts 文件中注册。

// 文件名: contracts/ally.ts
declare module '@ioc:Adonis/Addons/Ally' {
  interface SocialProviders {
    github: {
      config: GithubDriverConfig
      implementation: GithubDriverContract
    }
    twitter: {
      config: TwitterDriverConfig
      implementation: TwitterDriverContract
    }
  }
}

一旦你在合约文件中添加了新的辅助器,TypeScript 编译器将自动验证配置文件,强制你为它定义配置。

验证请求

在设置完成之后,你可以使用 ctx.ally 属性访问路由处理程序中的 ally 对象,并将用户重定向到 OAuth 提供者网站。

Route.get('/github/redirect', async ({ ally }) => {
  return ally.use('github').redirect()
})

处理回调请求

一旦用户决定允许/拒绝登录请求,OAuth 提供者会将用户重定向回 callbackUrl

在此路由中,你必须在访问用户之前处理失败状态的所有用例。

Route.get('/github/callback', async ({ ally }) => {
  const github = ally.use('github')

  /**
   * 用户已明确拒绝登录请求
   */
  if (github.accessDenied()) {
    return 'Access was denied'
  }

  /**
   * 无法验证 CSRF 状态
   */
  if (github.stateMisMatch()) {
    return 'Request expired. Retry again'
  }

  /**
   * 重定向过程中出现未知错误
   */
  if (github.hasError()) {
    return github.getError()
  }

  /**
   * 最后,访问用户
   */
  const user = await github.user()
})

将用户标记为已登录

Ally 将自身与你的应用程序使用的身份验证流程分离。它唯一的工作是管理 OAuth 请求的生命周期,并提供 OAuth 提供者共享的用户详细信息。

获得用户详细信息后,你可以决定如何存储它们并在应用程序中对用户进行身份验证。例如:在 GitHub 登录后,你可以创建一个新的用户帐户,然后使用 web authentication guard 创建一个会话。例如:

import User from 'App/Models/User'
import Route from '@ioc:Adonis/Core/Route'

Route.get('/github/callback', async ({ ally, auth }) => {
  const github = ally.use('github')

  /**
   * 管理错误状态
   */

  const githubUser = await github.user()

  /**
   * 通过电子邮件查找用户或创建
   * 一个新用户
   */
  const user = await User.firstOrCreate({
    email: githubUser.email,
  }, {
    name: githubUser.name,
    accessToken: githubUser.token.token,
    isVerified: githubUser.emailVerificationState === 'verified'
  })

  /**
   * 使用 web Guard 登录用户
   */
  await auth.use('web').login(user)
})

定义范围

你可以通过将回调传递给 redirect 方法来定义 OAuth 范围。回调接收重定向请求作为第一个参数,你可以使用 redirectRequest.scopes 方法定义范围。

注意:
你还可以在 config/ally.ts 文件中配置相同的作用域集合,我们将在所有重定向请求中使用它们。

{
  github: {
    driver: 'github',
    // ... 其余的配置
    scopes: ['user:email']
  }
}
Route.get('/github/redirect', async ({ ally }) => {
  return ally
    .use('github')
    .redirect((redirectRequest) => {
      redirectRequest.scopes(['gist', 'user'])
    })
})

作用域将根据底层 OAuth 提供程序而有所不同。但是,你可以依靠 TypeScript IntelliSense 列出所有可用选项。

此外,对于某些驱动程序(例如 Google),范围列表太长,因此我们不会为所有驱动程序提供 IntelliSense,你必须查阅 OAuth 辅助器文档。

注意:
如果要自定义 Discord 驱动程序,则需要存在 identify 范围才能正常工作。
你可以在 此处 找到有关它的更多信息。

定义其他查询字符串参数

你还可以使用 redirectRequest.param 方法在重定向请求上定义自定义查询字符串参数。例如:为 Google 辅助器定义 promptaccess_type

Route.get('/google/redirect', async ({ ally }) => {
  return ally
    .use('google')
    .redirect((redirectRequest) => {
      redirectRequest
        .param('access_type', 'offline')
        .param('prompt', 'select_account')
    })
})

用户属性

以下是 ally.user 方法返回的用户属性列表。所有底层驱动程序的属性都是一致的,你可以使用 user.original 属性访问原始响应。

const user = await ally.use('github').user()

console.log(user.id)
console.log(user.email)
// 等等

id

OAuth 辅助器返回的唯一 ID。


nickName

nickName 属性是指 OAuth 提供者的公开可见名称。name 属性的值在没有不同昵称时使用。


name

OAuth 辅助器响应中返回的用户名。


email

用户的关联电子邮件地址。


emailVerificationState

查看用户的电子邮件地址是否已通过 OAuth 提供商验证。状态总是以下之一。

  • verified 表示邮件已通过验证。
  • unverified 表示电子邮件未通过 OAuth 提供商验证。
  • unsupported 表示 OAuth 提供商不共享电子邮件是否经过验证。例如,Twitter 不共享电子邮件的验证状态。

avatarUrl

用户公开个人资料图片的 HTTP(s) URL。


token

token 属性是对底层访问令牌对象的引用。令牌对象具有以下子属性。

属性 协议 描述
token Oauth2 and Oauth1 访问令牌的值
secret Oauth1 令牌秘密。目前,Twitter 是唯一使用 Oauth1.0 的提供商
type Oauth2 令牌的类型
refreshToken Oauth2 仅当底层提供程序支持刷新令牌时才存在
expiresAt Oauth2 luxon DateTime 类的实例,表示访问令牌到期的绝对时间
expiresIn Oauth2 令牌将过期的秒数。它是一个静态值,不会随着时间的推移而改变

original

引用来自 OAuth 提供者的原始响应。

const githubUser = await github.user()
console.log(githubUser.original)

从令牌中获取用户

你可以使用 ally.userFromToken 方法从预先存在的访问令牌中检索用户详细信息。

Route.get('/github/user', async ({ ally }) => {
  const user = await ally
    .use('github')
    .userFromToken(accessToken)
})

对于 OAuth1 驱动程序(即 Twitter),你可以使用 tokensecret 值获取用户详细信息。

Route.get('/twitter/user', async ({ ally }) => {
  const user = await ally
    .use('twitter')
    .userFromTokenAndSecret(token, secret)
})

无状态认证

OAuth 请求(重定向 + 回调)的生命周期是有状态的,因为它将 CSRF 令牌存储在 cookie 中。但是,你可以通过调用 stateless 方法禁用 CSRF 验证。

Route.get('/github/redirect', async ({ ally }) => {
  return ally.use('github').stateless().redirect()
})

此外,请确保在检索用户时禁用 CSRF 验证。

Route.get('/github/callback', async ({ ally }) => {
  const github = ally.use('github').stateless()

  const user = await github.user()
})

其他方法/属性

以下是其他可用方法和属性的列表。

redirectUrl

redirectUrl 方法将重定向 URL 作为字符串返回。如果你决定通过首先获取重定向 URL 手动执行重定向,我们将不定义 state

const url = await ally.use('github').redirectUrl()

accessToken

通过与 OAuth 提供者交换后重定向代码来返回访问令牌。user 方法都包含访问令牌,因此无需单独获取。

const token = await ally.use('github').accessToken()

hasCode

查找重定向请求是否有授权码。

if(ally.use('github').hasCode()) {
}

getCode

返回授权码。

if(ally.use('github').hasCode()) {
  console.log(ally.use('github').getCode())
}

配置参考

以下是所有官方可用驱动程序的可用配置选项列表。

GitHub

github: {
  driver: 'github',
  clientId: '',
  clientSecret: '',
  callbackUrl: '',

  // GitHub 特有
  login: 'adonisjs',
  scopes: ['user', 'gist'],
  allowSignup: true,
}

Google

google: {
  driver: 'google',
  clientId: '',
  clientSecret: '',
  callbackUrl: '',

  // Google 特有
  prompt: 'select_account',
  accessType: 'offline',
  hostedDomain: 'adonisjs.com',
  display: 'page',
  scopes: ['userinfo.email', 'calendar.events'],
}

Twitter

twitter: {
  driver: 'twitter',
  clientId: '',
  clientSecret: '',
  callbackUrl: '',
}

Discord

google: {
  driver: 'discord',
  clientId: '',
  clientSecret: '',
  callbackUrl: '',

  // Discord 特有
  prompt: 'consent' | 'none',
  guildId: '',
  disableGuildSelect: false,
  permissions: 10,
  // 始终需要验证范围
  scopes: ['identify', 'email'],
}

LinkedIn

google: {
  driver: 'linkedin',
  clientId: '',
  clientSecret: '',
  callbackUrl: '',

  // LinkedIn 特有
  scopes: ['r_emailaddress', 'r_liteprofile'],
}

Facebook

google: {
  driver: 'facebook',
  clientId: '',
  clientSecret: '',
  callbackUrl: '',

  // Facebook 特有
  scopes: ['email', 'user_photos'],
  userFields: ['first_name', 'picture', 'email'],
  display: '',
  authType: '',
}

Spotify

google: {
  driver: 'spotify',
  clientId: '',
  clientSecret: '',
  callbackUrl: '',

  // Spotify 特有
  scopes: ['user-read-email', 'streaming'],
  showDialog: false
}

添加自定义驱动

Ally 是可扩展的,还允许添加自定义驱动程序。我们创建了一个 boilerplate repo 来帮助你从头开始创建自定义驱动程序并将其作为包发布到 npm 上。

本文章首发在 LearnKu.com 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://learnku.com/docs/adonisjs/5.x/au...

译文地址:https://learnku.com/docs/adonisjs/5.x/au...

上一篇 下一篇
贡献者:1
讨论数量: 0
发起讨论 只看当前版本


暂无话题~