Skip to content

Instantly share code, notes, and snippets.

@adeperio
Last active January 8, 2022 20:06
Show Gist options
  • Select an option

  • Save adeperio/73ce6680d4b80b45e624ab62bacfbdca to your computer and use it in GitHub Desktop.

Select an option

Save adeperio/73ce6680d4b80b45e624ab62bacfbdca to your computer and use it in GitHub Desktop.

Revisions

  1. adeperio revised this gist Feb 22, 2018. 1 changed file with 11 additions and 7 deletions.
    18 changes: 11 additions & 7 deletions main.js
    Original file line number Diff line number Diff line change
    @@ -22,16 +22,20 @@ app.on("ready", () => {
    let authService = new AuthService(getAuthConfig())
    let authWindow = new BrowserWindow({ width: 800, height: 600 })

    //Go to hosted login page at the authorise endpoint
    //authenticate
    //and request auth code, and send challenge
    /*
    Go to hosted login page at the authorise endpoint
    authenticate
    and request auth code, and send challenge
    */
    authWindow.loadURL(authService.requestAuthCode());

    authWindow.webContents.on('did-get-redirect-request', function(event, oldUrl, newUrl) {
    //after successfuly authenticating
    //get auth code from the redirect uri
    //and use that and the code verifier
    //to request an access code
    /*
    after successfuly authenticating
    get auth code from the redirect uri
    and use that and the code verifier
    to request an access code
    */
    authService.requestAccessCode(newUrl)
    });
    });
  2. adeperio revised this gist Feb 22, 2018. 1 changed file with 9 additions and 1 deletion.
    10 changes: 9 additions & 1 deletion main.js
    Original file line number Diff line number Diff line change
    @@ -21,9 +21,17 @@ app.on("ready", () => {

    let authService = new AuthService(getAuthConfig())
    let authWindow = new BrowserWindow({ width: 800, height: 600 })

    //Go to hosted login page at the authorise endpoint
    //authenticate
    //and request auth code, and send challenge
    authWindow.loadURL(authService.requestAuthCode());

    authWindow.webContents.on('did-get-redirect-request', function(event, oldUrl, newUrl) {

    //after successfuly authenticating
    //get auth code from the redirect uri
    //and use that and the code verifier
    //to request an access code
    authService.requestAccessCode(newUrl)
    });
    });
  3. adeperio revised this gist Feb 22, 2018. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion AuthService.js
    Original file line number Diff line number Diff line change
    @@ -42,7 +42,8 @@ export default class AuthService {

    return rp(options)
    .then(function(response) {
    //TODO: return / store access code
    //TODO: return / store access code,
    //remove console.log, meant for demonstration purposes only
    console.log('access token.response: ' + JSON.stringify(response));
    })
    .catch(function (err) {
  4. adeperio revised this gist Feb 22, 2018. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions AuthService.js
    Original file line number Diff line number Diff line change
    @@ -42,6 +42,7 @@ export default class AuthService {

    return rp(options)
    .then(function(response) {
    //TODO: return / store access code
    console.log('access token.response: ' + JSON.stringify(response));
    })
    .catch(function (err) {
  5. adeperio revised this gist Feb 22, 2018. No changes.
  6. adeperio revised this gist Feb 22, 2018. No changes.
  7. adeperio created this gist Feb 22, 2018.
    109 changes: 109 additions & 0 deletions AuthService.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,109 @@
    //@flow
    import request from 'request'
    import crypto from 'crypto'
    import rp from 'request-promise'

    export type AuthServiceConfig = {
    authorizeEndpoint: string,
    clientId: string,
    audience: string,
    scope: string,
    redirectUri: string,
    tokenEndpoint: string
    }

    //https://auth0.com/docs/api-auth/tutorials/authorization-code-grant-pkce
    export default class AuthService {

    challengePair : { verifier: string, challenge: string }
    config: AuthServiceConfig

    constructor(config: AuthServiceConfig){
    this.config = config
    }

    requestAuthCode() : string {
    this.challengePair = AuthService.getPKCEChallengePair()
    return this.getAuthoriseUrl(this.challengePair)
    }

    requestAccessCode(callbackUrl: string): Promise<any> {

    return new Promise((resolve, reject) => {

    if(this.isValidAccessCodeCallBackUrl(callbackUrl)) {

    let authCode = AuthService.getParameterByName('code', callbackUrl)

    if(authCode != null){

    let verifier = this.challengePair.verifier
    let options = this.getTokenPostRequest(authCode, verifier)

    return rp(options)
    .then(function(response) {
    console.log('access token.response: ' + JSON.stringify(response));
    })
    .catch(function (err) {
    if (err) throw new Error(err);
    });
    } else {
    reject('Could not parse the authorization code')
    }

    } else {
    reject('Access code callback url not expected.')
    }
    })
    }

    getAuthoriseUrl(challengePair: { verifier: string, challenge: string }) : string {
    return `${this.config.authorizeEndpoint}?audience=${this.config.audience}&scope=${this.config.scope}&response_type=code&client_id=${this.config.clientId}&code_challenge=${challengePair.challenge}&code_challenge_method=S256&redirect_uri=${this.config.redirectUri}`
    }
    getTokenPostRequest(authCode: string, verifier: string){
    return {
    method: 'POST',
    url: this.config.tokenEndpoint,
    headers: { 'content-type': 'application/json' },
    body: `{"grant_type":"authorization_code",
    "client_id": "${this.config.clientId}",
    "code_verifier": "${verifier}",
    "code": "${authCode}",
    "redirect_uri":"${this.config.redirectUri}"
    }`
    };
    }

    isValidAccessCodeCallBackUrl(callbackUrl: string) : boolean {
    return callbackUrl.indexOf(this.config.redirectUri) > -1
    }

    static getPKCEChallengePair() : { verifier: string, challenge: string } {
    let verifier = AuthService.base64URLEncode(crypto.randomBytes(32));
    let challenge = AuthService.base64URLEncode(AuthService.sha256(verifier));
    return { verifier, challenge };
    }

    static getParameterByName(name: string, url: string) : ?string {

    name = name.replace(/[\[\]]/g, "\\$&");
    var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
    results = regex.exec(url);
    if (!results) return null;
    if (!results[2]) return '';
    return decodeURIComponent(results[2].replace(/\+/g, " "));
    }

    static base64URLEncode(str: Buffer) : string {

    return str.toString('base64')
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=/g, '');
    }

    static sha256(buffer: string) : Buffer {
    return crypto.createHash('sha256').update(buffer).digest();
    }
    }
    39 changes: 39 additions & 0 deletions main.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,39 @@
    import path from "path";
    import events from 'events'
    import { app, BrowserWindow } from "electron";
    import AuthService, { AuthServiceConfig } from "./AuthService"


    function getAuthConfig(){
    //sample values - plug your Auth0 config here
    var authConfig : AuthServiceConfig = {
    clientId: 'rlasjf82130948asdkfjaslsaklaskfd',
    authorizeEndpoint: 'https://myapp.auth0.com/authorize',
    audience: 'https://myapi.com:8080',
    scope: 'email%20given_name%20profile',
    redirectUri: 'https://myapp.auth0.com/mobile',
    tokenEndpoint: 'https://myapp.auth0.com/oauth/token'
    }
    return authConfig
    }

    app.on("ready", () => {

    let authService = new AuthService(getAuthConfig())
    let authWindow = new BrowserWindow({ width: 800, height: 600 })
    authWindow.loadURL(authService.requestAuthCode());
    authWindow.webContents.on('did-get-redirect-request', function(event, oldUrl, newUrl) {

    authService.requestAccessCode(newUrl)
    });
    });

    app.on('window-all-closed', () => {
    // Respect the OSX convention of having the application in memory even
    // after all windows have been closed
    if (process.platform !== 'darwin') {
    app.quit();
    }
    });