Initial test

This commit is contained in:
Ananay Arora
2019-06-14 01:14:07 +00:00
parent 1684cb5682
commit 9e9e7737a1
5 changed files with 220 additions and 4 deletions
+2 -1
View File
@@ -1 +1,2 @@
node_modules node_modules
.AppleDouble
View File
+4 -3
View File
@@ -1,8 +1,8 @@
{ {
"name": "passport-apple", "name": "passport-apple",
"version": "0.0.1", "version": "0.0.2",
"description": "Passport strategy for Sign in with Apple", "description": "Passport strategy for Sign in with Apple",
"main": "index.js", "main": "src/strategy.js",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },
@@ -31,6 +31,7 @@
}, },
"homepage": "https://github.com/ananay/passport-apple#readme", "homepage": "https://github.com/ananay/passport-apple#readme",
"dependencies": { "dependencies": {
"passport-oauth2": "^1.5.0" "passport-oauth2": "^1.5.0",
"jsonwebtoken": "^8.5.1"
} }
} }
+124
View File
@@ -0,0 +1,124 @@
/**
* Passport Strategy that implements "Sign in with Apple"
* @author: Ananay Arora <[email protected]>
*/
const OAuth2Strategy = require('passport-oauth2'),
crypto = require('crypto'),
AppleClientSecret = require("./token"),
util = require('util')
querystring = require('querystring'),
jwt = require('jsonwebtoken');
/**
* Passport Strategy Constructor
*
* Example:
*
* passport.use(new AppleStrategy({
* clientID: "",
* teamID: "",
* callbackURL: "",
* keyID: "",
* privateKeyLocation: ""
* }, function(accessToken, refreshToken, idToken, profile, cb) {
* // Here, check if the idToken exists in your database!
* cb(null, idToken);
* }));
*
* @param {object} options - Configuration options
* @param {string} options.clientID – Client ID (also known as the Services ID
* in Apple's Developer Portal). Example: com.ananayarora.app
* @param {string} options.teamID – Team ID for the Apple Developer Account
* found on top right corner of the developers page
* @param {string} options.keyID – The identifier for the private key on the Apple
* Developer Account page
* @param {string} options.callbackURL – The identifier for the private key on the Apple
* Developer Account page
* @param {string} options.privateKeyLocation - Location to the private key
* @param {function} verify
* @access public
*/
function Strategy(options, verify) {
// Set the URLs
options = options || {};
options.authorizationURL = options.authorizationURL || 'https://appleid.apple.com/auth/authorize';
options.tokenURL = options.tokenURL || 'https://appleid.apple.com/auth/token';
// Make the OAuth call
OAuth2Strategy.call(this, options, verify);
this.name = 'apple';
// Initiliaze the client_secret generator
const _tokenGenerator = new AppleClientSecret({
"client_id": options.clientID,
"team_id": options.teamID,
"key_id": options.keyID
}, options.privateKeyLocation);
// Get the OAuth Access Token from Apple's server
// using the grant code / refresh token.
this._oauth2.getOAuthAccessToken = function(code, params, callback) {
// Generate the client_secret using the library
_tokenGenerator.generate().then((client_secret) => {
params = params || {};
const codeParam = params.grant_type === 'refresh_token' ? 'refresh_token' : 'code';
params[codeParam] = code;
params['client_id'] = this._clientId;
params['client_secret'] = client_secret;
const post_data = querystring.stringify(params);
const post_headers = {
'Content-Type': 'application/x-www-form-urlencoded'
};
this._request(
'POST',
this._getAccessTokenUrl(),
post_headers,
post_data,
null,
function(error, data, response) {
if (error) {
callback(error);
} else {
let results = JSON.parse(data);
let access_token = results.access_token;
let refresh_token = results.refresh_token;
let id_token = jwt.decode(results.id_token).sub;
callback(null, access_token, refresh_token, id_token, results);
}
}
)
}).catch((error) => {
// Log to the console if the token generation fails.
console.log(error);
});
}
}
// Inherit from `OAuth2Strategy`.
util.inherits(Strategy, OAuth2Strategy);
/**
* Process the authentication request
* @param {http.IncomingMessage} req
* @param {object} options
* @access protected
*/
Strategy.prototype.authenticate = function(req, options) {
OAuth2Strategy.prototype.authenticate.call(this, req, options);
};
/**
* Modify the authorization params. Currently adds
* the missing `state` parameter
* @param {object} options
* @access protected
*/
Strategy.prototype.authorizationParams = function (options) {
options.state = crypto.randomBytes(5).toString('hex');
return options;
}
module.exports = Strategy;
+90
View File
@@ -0,0 +1,90 @@
/**
* Generates a client secret for Apple auth
* using the private key.
* @author: Ananay Arora <[email protected]>
*/
const jwt = require('jsonwebtoken');
const fs = require('fs');
class AppleClientSecret {
/**
*
* @param {object} config
* @param {string} config.client_id
* @param {string} config.team_id
* @param {string} config.redirect_uri
* @param {string} config.key_id
* @param {*} privateKeyLocation
*/
constructor(config, privateKeyLocation) {
this._config = config;
this._privateKeyLocation = privateKeyLocation;
this.generate = this.generate.bind(this);
this._generateToken = this._generateToken.bind(this);
}
/**
* Generates the JWT token
* @param {string} clientId
* @param {string} teamId
* @param {string} privateKey
* @param {int} expiration
* @returns {Promise<string>} token
*/
_generateToken(clientId, teamId, privateKey, exp, keyid) {
return new Promise (
(resolve, reject) => {
// Curate the claims
const claims = {
iss: teamId,
iat: Math.floor(Date.now() / 1000),
exp,
aud: 'https://appleid.apple.com',
sub: clientId,
};
// Sign the claims using the private key
jwt.sign(claims, privateKey, {
algorithm: 'ES256',
keyid
}, (err, token) => {
if (err) {
reject("AppleAuth Error – Error occurred while signing: " + err);
}
resolve(token);
});
}
);
}
/**
* Reads the private key file calls
* the token generation method
* @returns {Promise<string>} token - The generated client secret
*/
generate() {
return new Promise (
(resolve, reject) => {
fs.readFile(this._privateKeyLocation, (err, privateKey) => {
if (err) {
reject("AppleAuth Error - Couldn't read your Private Key file: " + err);
}
let exp = Math.floor(Date.now() / 1000) + ( 86400 * 180 ); // Make it expire within 6 months
this._generateToken(
this._config.client_id,
this._config.team_id,
privateKey,
exp,
this._config.key_id
).then((token) => {
resolve(token);
}).catch((err) => {
reject(err);
});
});
}
);
}
}
module.exports = AppleClientSecret;