首次完整推送,
V:1.20240808.006
This commit is contained in:
@ -0,0 +1,79 @@
|
||||
const rsaPublicKeyPem = require('../rsa-public-key-pem')
|
||||
const {
|
||||
jwtVerify
|
||||
} = require('../../../npm/index')
|
||||
let authKeysCache = null
|
||||
|
||||
module.exports = class Auth {
|
||||
constructor (options) {
|
||||
this.options = Object.assign({
|
||||
baseUrl: 'https://appleid.apple.com',
|
||||
timeout: 10000
|
||||
}, options)
|
||||
}
|
||||
|
||||
async _fetch (url, options) {
|
||||
const { baseUrl } = this.options
|
||||
return uniCloud.httpclient.request(baseUrl + url, options)
|
||||
}
|
||||
|
||||
async verifyIdentityToken (identityToken) {
|
||||
// 解密出kid,拿取key
|
||||
const jwtHeader = identityToken.split('.')[0]
|
||||
const { kid } = JSON.parse(Buffer.from(jwtHeader, 'base64').toString())
|
||||
let authKeys
|
||||
if (authKeysCache) {
|
||||
authKeys = authKeysCache
|
||||
} else {
|
||||
authKeys = await this.getAuthKeys()
|
||||
authKeysCache = authKeys
|
||||
}
|
||||
const usedKey = authKeys.find(item => item.kid === kid)
|
||||
|
||||
/**
|
||||
* identityToken 格式
|
||||
*
|
||||
* {
|
||||
* iss: 'https://appleid.apple.com',
|
||||
* aud: 'io.dcloud.hellouniapp',
|
||||
* exp: 1610626724,
|
||||
* iat: 1610540324,
|
||||
* sub: '000628.30119d332d9b45a3be4a297f9391fd5c.0403',
|
||||
* c_hash: 'oFfgewoG36cJX00KUbj45A',
|
||||
* email: 'x2awmap99s@privaterelay.appleid.com',
|
||||
* email_verified: 'true',
|
||||
* is_private_email: 'true',
|
||||
* auth_time: 1610540324,
|
||||
* nonce_supported: true
|
||||
* }
|
||||
*/
|
||||
const payload = jwtVerify(
|
||||
identityToken,
|
||||
rsaPublicKeyPem(usedKey.n, usedKey.e),
|
||||
{
|
||||
algorithms: usedKey.alg
|
||||
}
|
||||
)
|
||||
|
||||
if (payload.iss !== 'https://appleid.apple.com' || payload.aud !== this.options.bundleId) {
|
||||
throw new Error('Invalid identity token')
|
||||
}
|
||||
|
||||
return {
|
||||
openid: payload.sub,
|
||||
email: payload.email,
|
||||
emailVerified: payload.email_verified === 'true',
|
||||
isPrivateEmail: payload.is_private_email === 'true'
|
||||
}
|
||||
}
|
||||
|
||||
async getAuthKeys () {
|
||||
const { status, data } = await this._fetch('/auth/keys', {
|
||||
method: 'GET',
|
||||
dataType: 'json',
|
||||
timeout: this.options.timeout
|
||||
})
|
||||
if (status !== 200) throw new Error('request https://appleid.apple.com/auth/keys fail')
|
||||
return data.keys
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
// http://stackoverflow.com/questions/18835132/xml-to-pem-in-node-js
|
||||
/* eslint-disable camelcase */
|
||||
function rsaPublicKeyPem (modulus_b64, exponent_b64) {
|
||||
const modulus = Buffer.from(modulus_b64, 'base64')
|
||||
const exponent = Buffer.from(exponent_b64, 'base64')
|
||||
|
||||
let modulus_hex = modulus.toString('hex')
|
||||
let exponent_hex = exponent.toString('hex')
|
||||
|
||||
modulus_hex = prepadSigned(modulus_hex)
|
||||
exponent_hex = prepadSigned(exponent_hex)
|
||||
|
||||
const modlen = modulus_hex.length / 2
|
||||
const explen = exponent_hex.length / 2
|
||||
|
||||
const encoded_modlen = encodeLengthHex(modlen)
|
||||
const encoded_explen = encodeLengthHex(explen)
|
||||
const encoded_pubkey = '30' +
|
||||
encodeLengthHex(
|
||||
modlen +
|
||||
explen +
|
||||
encoded_modlen.length / 2 +
|
||||
encoded_explen.length / 2 + 2
|
||||
) +
|
||||
'02' + encoded_modlen + modulus_hex +
|
||||
'02' + encoded_explen + exponent_hex
|
||||
|
||||
const der_b64 = Buffer.from(encoded_pubkey, 'hex').toString('base64')
|
||||
|
||||
const pem = '-----BEGIN RSA PUBLIC KEY-----\n' +
|
||||
der_b64.match(/.{1,64}/g).join('\n') +
|
||||
'\n-----END RSA PUBLIC KEY-----\n'
|
||||
|
||||
return pem
|
||||
}
|
||||
|
||||
function prepadSigned (hexStr) {
|
||||
const msb = hexStr[0]
|
||||
if (msb < '0' || msb > '7') {
|
||||
return '00' + hexStr
|
||||
} else {
|
||||
return hexStr
|
||||
}
|
||||
}
|
||||
|
||||
function toHex (number) {
|
||||
const nstr = number.toString(16)
|
||||
if (nstr.length % 2) return '0' + nstr
|
||||
return nstr
|
||||
}
|
||||
|
||||
// encode ASN.1 DER length field
|
||||
// if <=127, short form
|
||||
// if >=128, long form
|
||||
function encodeLengthHex (n) {
|
||||
if (n <= 127) return toHex(n)
|
||||
else {
|
||||
const n_hex = toHex(n)
|
||||
const length_of_length_byte = 128 + n_hex.length / 2 // 0x80+numbytes
|
||||
return toHex(length_of_length_byte) + n_hex
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = rsaPublicKeyPem
|
Reference in New Issue
Block a user