strategy.js

var util = require('util'),
	querystring = require('querystring'),
	OAuth2Strategy = require('passport-oauth').OAuth2Strategy;

/**
 * `Strategy` constructor.
 *
 * This authentication strategy authenticates and authorizes requests by using
 * the new "Sign in with Apple" service. It uses the OAuth 2.0 protocol.
 *
 * Applications must supply a `verify` callback which accepts an `accessToken`,
 * `refreshToken` and service-specific `profile`, and then calls the `done`
 * callback supplying a `user`, which should be set to `false` if the
 * credentials are not valid.  If an exception occured, `err` should be set.
 *
 * Options:
 *   - `clientID`      your Apple client id
 *   - `clientSecret`  your Apple client secret
 *   - `callbackURL`   URL to which Apple will redirect the user
 *                     after granting authorization
 *   - `scope`         [Optional] An array of named scopes
 *
 *
 * @param {Object} options The options for the strategy.
 * @param {string} options.clientID Your Apple client id.
 * @param {string} options.clientSecret Your Apple client secret.
 * @param {string} options.callbackURL URL to which Apple will redirect the user after granting authorization.
 * @param {Array.<string>} options.scope An array of named scopes
 * @param {Function} verify The function to verify the user against a database in.
 *
 * @class  AppleSignInStrategy
 * @example
 * passport.use(new AppleSignInStrategy({
 *     clientID: 'app key',
 *     clientSecret: 'app secret'
 *         callbackURL: 'https://www.example.com/auth/apple-sign-in/callback'
 *     },
 *     function(accessToken, refreshToken, profile, done) {
 *         User.findOrCreate(..., function (err, user) {
 *             done(err, user);
 *         });
 *     }
 * ));
 */
function AppleSignInStrategy(options, verify) {
	options = options || {};
	options.authorizationURL =
		options.authorizationURL || 'https://appleid.apple.com/auth/authorize';
	options.tokenURL =
		options.tokenURL || 'https://appleid.apple.com/auth/token';
	options.scopeSeparator = options.scopeSeparator || ' ';

	OAuth2Strategy.call(this, options, verify);
	this.name = 'apple-sign-in';
	this._userProfileURL = options.userProfileURL; // We don't know about this yet, at least according to documentation

	this._oauth2.getOAuthAccessToken = function(code, params, callback) {
		params = params || {};
		var codeParam =
			params.grant_type === 'refresh_token' ? 'refresh_token' : 'code';
		params[codeParam] = code;
		params['client_id'] = this._clientId;
		params['client_secret'] = this._clientSecret;

		var body = querystring.stringify(params);
		var headers = { 'Content-Type': 'application/x-www-form-urlencoded' };

		this._request(
			'POST',
			this._getAccessTokenUrl(),
			headers,
			body,
			null,
			function(error, data, response) {
				if (error) {
					callback(error);
				} else {
					var data = JSON.parse(data);

					callback(
						null,
						data.access_token,
						data.refresh_token,
						data.expires_in,
						data
					);
				}
			}
		);
	};
}

/**
 * Inherit from `OAuth2Strategy`.
 */
util.inherits(AppleSignInStrategy, OAuth2Strategy);

/**
 * Expose `Strategy`.
 */
module.exports = AppleSignInStrategy;