const rsaPublicKeyPem = require('../rsa-public-key-pem') 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 = require('jsonwebtoken').verify( 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 } }