Coder Social home page Coder Social logo

techofficer / node-apple-signin Goto Github PK

View Code? Open in Web Editor NEW
53.0 2.0 39.0 31 KB

Node.JS wrapper around Sign In with Apple REST API

License: MIT License

JavaScript 100.00%
nodejs node node-js oauth oauth2 oauth2-client oauth2-authentication apple ios

node-apple-signin's Introduction

[Node.js] Sign in with Apple

Node.JS wrapper around Sign in with Apple REST API.

This module lets you authenticate users using Apple account in your Node.js application.

Prerequisites

  1. You should be enrolled in Apple Developer Program.
  2. Please have a look at Apple documentation related to "Sign in with Apple" feature.
  3. You should create App ID and Service ID in your Apple Developer Account.
  4. You should generate private key for your Service ID in your Apple Developer Account.

More detail about configuration can be found in blog post and Apple docs.

Installation

Install the module using npm:

npm install --save apple-signin

Usage

1. Get authorization URL

Start "Sign in with Apple" flow by redirecting user to the authorization URL.

const appleSignin = require("apple-signin");

const options = {
    clientID: "com.gotechmakers.auth.client", // identifier of Apple Service ID.
    redirectUri: "http://localhost:3000/auth/apple/callback",
    state: "123", // optional, An unguessable random string. It is primarily used to protect against CSRF attacks.
    scope: "email" // optional, default value is "email".
};

const authorizationUrl = appleSignin.getAuthorizationUrl(options);

Alternatively, you can use Sign In with Apple browser javascript library.

2. Get access token

2.1. Retrieve "code" query param from URL string when user is redirected to your site after successful sign in with Apple. Example: http://localhost:3000/auth/apple/callback?code=somecode&state=123.

2.2. Exchange retrieved "code" to user's access token.

More detail can be found in Apple docs.

const clientSecret = appleSignin.getClientSecret({
    clientID: "com.gotechmakers.auth.client", // identifier of Apple Service ID.
    teamId: "teamId", // Apple Developer Team ID.
    privateKeyPath: "/var/www/app/AuthKey_XXX.p8", // path to private key associated with your client ID.
    keyIdentifier: "XXX" // identifier of the private key.    
});

const options = {
    clientID: "com.gotechmakers.auth.client", // identifier of Apple Service ID.
    redirectUri: "http://localhost:3000/auth/apple/callback", // use the same value which you passed to authorisation URL.
    clientSecret: clientSecret
};
 
appleSignin.getAuthorizationToken(code, options).then(tokenResponse => {
    console.log(tokenResponse);
}).catch(error => {
    console.log(error);
});

Result of getAuthorizationToken command is a JSON object representing Apple's TokenResponse:

{
    access_token: "ACCESS_TOKEN", // A token used to access allowed data.
    token_type: 'Bearer', // It will always be Bearer.
    expires_in: 3600, // The amount of time, in seconds, before the access token expires.
    refresh_token: "REFRESH_TOKEN", // used to regenerate new access tokens. Store this token securely on your server.
    id_token: "ID_TOKEN" // A JSON Web Token that contains the user’s identity information.
}

3. Verify token signature and get unique user's identifier

appleSignin.verifyIdToken(tokenResponse.id_token, clientID).then(result => {
    const userAppleId = result.sub;
}).catch(error => {
    // Token is not verified
    console.log(error);
});

4. Refresh access token after expiration

const clientSecret = appleSignin.getClientSecret({
    clientID: "com.gotechmakers.auth.client", // identifier of Apple Service ID.
    teamId: "teamId", // Apple Developer Team ID.
    privateKeyPath: "/var/www/app/AuthKey_XXX.p8", // path to private key associated with your client ID.
    keyIdentifier: "XXX" // identifier of the private key.    
});

const options = {
    clientID: "com.gotechmakers.auth.client", // identifier of Apple Service ID.
    clientSecret: clientSecret
};
 
appleSignin.refreshAuthorizationToken(refreshToken, options).then(result => {
    const newAccessToken = result.access_token;
}).catch(error => {
    console.log(error);
})

Examples

Developers using the popular Express web framework can refer to an example as a starting point for their own web applications.

You can also check live example

Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

License

The MIT License

Copyright (c) 2019 Artem Efremov https://gotechmakers.com

Support

If you have any questions or need help with integration, then you can contact me by email [email protected].

node-apple-signin's People

Contributors

techofficer avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

node-apple-signin's Issues

redirectUri should be optional on getAuthorizationToken

The getAuthorizationToken function expects a redirectUri. If it's not supplied, it throws an error with the message redirectUri is empty.

While passing the redirectUri is required when doing sign in through the web, it doesn't exist when the authentication happens natively through an iOS app. For this reason, this field should be optional.

Request missing required parameter or unsupported parameters or invalid redirect url

https://developer.apple.com/forums/thread/121760

Use response_mode = form_post when requesting name or email scope

export const getAuthorizationUrl = (options: any = {}) => {
if (!options.clientID) {
throw Error('clientID is empty');
}
if (!options.redirectUri) {
throw Error('redirectUri is empty');
}
return ENDPOINT_URL + '/auth/authorize?' + qs.stringify({
response_mode: 'form_post',
response_type: 'code',
state: options.state || 'state',
client_id: options.clientID,
redirect_uri: options.redirectUri,
scope: options.scope,
});
};

Getting error "invalid_grant"

Follow example and perform the following

   const clientSecret = appleSignin.getClientSecret({
        clientID: "com.xxx.client", // identifier of Apple Service ID.
        teamId: "xxxxxx", // Apple Developer Team ID.
        privateKeyPath: __dirname + "/authkey.p8", // path to private key associated with your client ID.
        keyIdentifier: "xxx" // identifier of the private key.
    });

    const options = {
        grant_type: 'authorization_code',
        clientID: "com.xxx.client", // identifier of Apple Service ID.
        redirectUri: "http://xxx.my/redirect", // use the same value which you passed to authorisation URL.
        clientSecret: clientSecret
    };

    appleSignin.getAuthorizationToken(accessToken, options).then(tokenResponse => {
        console.log(tokenResponse);
    }).catch(error => {
        console.log(error);
    });

accessToken is the getting from ios apps apple sign in

Private key must be a file

In order to deploy an app to Heroku or other PaaS, an app must be implemented using the twelve-factor methodology, which among other things, says that we shouldn't store credentials or sensitive information in our code repositories (III. Config). Instead, this kind of information should be stored in environment variables.

Currently, there's no way to read the private key content from an environment variable and pass it directly to node-apple-signin. Although we could copy the environment variable to a file during the app startup and pass the path to it, a better solution would be to pass the string directly.

Passing a string directly also avoids unnecessary disk reads, which are currently synchronous (fs.readFileSync is used) and can block the event loop.

jwt.verify - Invalid Signature caused by addition of new Apple Public Keys

Just a heads up to anyone using this library, as mentioned here: https://forums.developer.apple.com/thread/129047

Apple recently added multiple public keys instead of the single public key that has been available since Apple Sign In was launched (see: https://appleid.apple.com/auth/keys)

To avoid getting invalid signature errors every time a token is signed using a different key to the first returned from the URL above (which what this library currently uses) the following changes are needed:

const verifyIdToken = async (idToken, clientID) => {
const decodedToken = jwt.decode(identityToken, { complete: true });
const applePublicKey = await getAppleIDPublicKey(decodedToken.header.kid);

const jwtClaims = jwt.verify(idToken, applePublicKey, { algorithms: 'RS256' });
...

getAppleIDPublicKey then needs to use the kid (keyIdentifier) parameter to return the correct key from the list of keys returned from https://appleid.apple.com/auth/keys and everything should work 100% again 🥳

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.