OAuth

OAuth Procedure

令牌(token)与密码(password)的作用是一样的,都可以进入系统,但是有三点差异。

(1)令牌是短期的,到期会自动失效,用户自己无法修改。密码一般长期有效,用户不修改,就不会发生变化。

(2)令牌可以被数据所有者撤销,会立即失效。以上例而言,屋主可以随时取消快递员的令牌。密码一般不允许被他人撤销。

(3)令牌有权限范围(scope),比如只能进小区的二号门。对于网络服务来说,只读令牌就比读写令牌更安全。密码一般是完整权限。

上面这些设计,保证了令牌既可以让第三方应用获得权限,同时又随时可控,不会危及系统安全。这就是 OAuth 2.0 的优点。

Four ways to get the token

  • 授权码(authorization-code)
  • 隐藏式(implicit)
  • 密码式(password):
  • 客户端凭证(client credentials)

A simple demo about third-party login

In config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const GITHUB_OAUTH_URL = 'https://github.com/login/oauth/authorize'
const SCOPE = 'user'

const github = {
request_token_url: 'https://github.com/login/oauth/access_token',
client_id: '7719dcc449a12e7f83f4',
client_secret: '42771ea51b4a3afac6ab67cae5cd8b924f81c7fc',
}

module.exports = {
github,
GITHUB_OAUTH_URL,
OAUTH_URL: `${GITHUB_OAUTH_URL}?client_id=${github.client_id}&scope=${SCOPE}`,
}

In server/auth.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// 处理github返回的auth code
const axios = require('axios')
const config = require('../config')

const { client_id, client_secret, request_token_url } = config.github

module.exports = (server) => {
server.use(async (ctx, next) => {
if (ctx.path === '/auth') {
console.log(ctx.path)
const { code } = ctx.query//获取授权码
if (code) {
// 请求令牌
const result = await axios({
method: 'POST',
url: request_token_url,
data: {
client_id,
client_secret,
code,
},
headers: {
Accept: 'application/json',
},
})

// github 可能会在status是200的情况下返回error信息
if (result.status === 200 && (result.data && !result.data.error)) {
ctx.session.githubAuth = result.data

const { access_token, token_type } = result.data
// 获取用户信息
const { data: userInfo } = await axios({
method: 'GET',
url: 'https://api.github.com/user',
headers: {
Authorization: `${token_type} ${access_token}`,
},
})

ctx.session.userInfo = userInfo
// 重定向到登录前的页面或首页
ctx.redirect((ctx.session && ctx.session.urlBeforeOAuth) || '/')
ctx.session.urlBeforeOAuth = ''
} else {
ctx.body = `request token failed ${result.data && result.data.error}`
}
} else {
ctx.body = 'code not exist'
}
} else {
await next()
}
})
}