Code Monkey home page Code Monkey logo

js-pkce's Introduction

js-pkce

A package that makes using the OAuth2 PKCE flow easier

Installation

npm i js-pkce

Create a new instance

Create a new instance of js-pkce with all of the details needed.

import PKCE from 'js-pkce';
const pkce = new PKCE({
  client_id: 'myclientid',
  redirect_uri: 'http://localhost:8080/auth',
  authorization_endpoint: 'https://authserver.com/oauth/authorize',
  token_endpoint: 'https://authserver.com/oauth/token',
  requested_scopes: '*',
});

Start the authorization process

Typically you just need to go to the authorization url to start the process. This example is something that might work in a SPA.

window.location.replace(pkce.authorizeUrl());

You may add additional query parameters to the authorize url by using an optional second parameter:

const additionalParams = {test_param: 'testing'};
window.location.replace(pkce.authorizeUrl(additionalParams));

Trade the code for a token

After logging in with the authorization server, you will be redirected to the value in the redirect_uri parameter you set when creating the instance. Again, this is an example that might work for a SPA.

When you get back here, you need to exchange the code for a token.

const url = window.location.href;
pkce.exchangeForAccessToken(url).then((resp) => {
  const token = resp.access_token;
  // Do stuff with the access token.
});

As with the authorizeUrl method, an optional second parameter may be passed to the exchangeForAccessToken method to send additional parameters to the request:

const url = window.location.href;
const additionalParams = {test_param: 'testing'};

pkce.exchangeForAccessToken(url, additionalParams).then((resp) => {
  const token = resp.access_token;
  // Do stuff with the access token.
});

Refreshing the token

Get a new access token using a refresh token

pkce.refreshAccessToken(refreshToken).then((resp) => {
  const accessToken = resp.access_token;
  const refreshToken = resp.refresh_token;
  // Do stuff with the access & refresh token.
});

A note on Storage

By default, this package will use sessionStorage to persist the pkce_state. On (mostly) mobile devices there's a higher chance users are returning in a different browser tab. E.g. they kick off in a WebView & get redirected to a new tab. The sessionStorage will be empty there.

In this case it you can opt in to use localStorage instead of sessionStorage:

import PKCE from 'js-pkce';
const pkce = new PKCE({
  // ...
  storage: localStorage, // any Storage object, sessionStorage (default) or localStorage 
});

Cors credentials

When using httpOnly cookies, there is some additional configuration required. The method enableCorsCredentials can be called to allow sending credentials.

pkce.enableCorsCredentials(true);

js-pkce's People

Contributors

bennovakovic avatar bpedroza avatar dependabot[bot] avatar rudolfbyker avatar theberg avatar wietsewind 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

Watchers

 avatar  avatar

js-pkce's Issues

id token

Some providers (including Azure AD) provide Access Tokens which aren't JWT and contain no information - this is perfectly acceptable as per the standard RFC. But I see no way using your library to acquire the ID token?

Can you provide a method for accessing the ID token? E.g., exchangeForIDToken?

Thanks,

Not usable from browser extensions due to usage of Storage API

MV3 browser extensions only expose the StorageArea API (via chrome.storage.session for example), not the Storage API in service workers.

The StorageArea API is asynchronous so it is not possible to build a Storage API shim around it to pass into the js-pkce storage config variable either.

That means it's impossible to use this library from a Chrome extension currently.

  1. Can you think of a workaround?
  2. Would it be possible to support extensions / StorageArea directly?

Uncaught (in promise) Error: Cannot resolve "crypto" from ...

I'm getting the error in browser (not during compilation)

Uncaught (in promise) Error: Cannot resolve "crypto" from http://localhost:8091/main-903a0f08.js
    at a.resolve (s.min.js:3:4413)
    at s.min.js:3:1488
    at Array.map (<anonymous>)
    at s.min.js:3:1431

My package.json

{
    "private": true,
    "main": "dist/main.js",
    "name": "name",
    "scripts": {
      "test": "npm run build-web && ava",
      "test:lazy": "ava",
      "serve": "cross-env NODE_ENV=development rollup -c -w",
      "build-web": "cross-env NODE_ENV=production PLATFORM=web rollup -c",
      "build-cordova": "cross-env NODE_ENV=production PLATFORM=cordova rollup -c",
      "node-prod": "ts-node -O '{\"module\": \"commonjs\"}' src/server/"
    },
    "version": "0.0.1",
    "browserslist": "Firefox ESR",
    "ava": {
      "files": [
        "./test/specs/*.ts"
      ],
      "serial": false,
      "failFast": true,
      "timeout": "2m",
      "compileEnhancements": false,
      "extensions": [
        "ts"
      ],
      "require": [
        "ts-node/register"
      ]
    },
    "dependencies": {
      "body-parser": "^1.19.0",
      "compression": "^1.7.4",
      "crypto-browserify": "^3.12.0",
      "crypto-js": "^3.1.9",
      "date-fns": "^2.21.1",
      "element-ui": "^2.15.1",
      "email-regex": "^4.0.0",
      "express": "^4.17.1",
      "fela-vue": "2.6.0-beta.44",
      "js-pkce": "^1.2.1",
      "lafetch": "0.4.5",
      "node-fetch": "^2.6.1",
      "pepka": "^0.12.2",
      "portal-vue": "^2.1.7",
      "reset-css": "^5.0.1",
      "roboto-fontface": "^0.10.0",
      "systemjs": "^6.8.3",
      "ts-node": "^9.1.1",
      "vue": "^2.6.12",
      "vue-page-title": "^1.2.4",
      "vue-router": "^3.5.1",
      "vuex": "^3.6.2",
      "ws": "^7.4.5",
      "wspromisify": "^2.4.1"
    },
    "devDependencies": {
      "@babel/core": "^7.13.16",
      "@babel/plugin-proposal-object-rest-spread": "^7.13.8",
      "@babel/preset-env": "^7.13.15",
      "@rollup/plugin-commonjs": "^17.0.0",
      "@rollup/plugin-typescript": "8.2.1",
      "@rollup/plugin-url": "^6.0.0",
      "@types/express": "^4.17.11",
      "@types/node": "^14.14.41",
      "@types/node-fetch": "^2.5.10",
      "@types/ws": "^7.4.1",
      "@vue/babel-helper-vue-jsx-merge-props": "^1.2.1",
      "@vue/babel-plugin-transform-vue-jsx": "^1.2.1",
      "@vue/babel-preset-jsx": "^1.2.4",
      "ava": "^3.15.0",
      "babel-helper-vue-jsx-merge-props": "^2.0.3",
      "babel-plugin-syntax-jsx": "^6.18.0",
      "babel-plugin-transform-vue-jsx": "^3.7.0",
      "babel-preset-env": "^1.7.0",
      "cross-env": "^7.0.3",
      "fs-extra": "^9.1.0",
      "html-minifier": "^4.0.0",
      "mustache": "^4.2.0",
      "postcss": "^8.2.10",
      "regenerator-runtime": "^0.13.7",
      "rollup": "^2.45.2",
      "rollup-plugin-alias": "^2.2.0",
      "rollup-plugin-babel": "^4.4.0",
      "rollup-plugin-cleaner": "^1.0.0",
      "rollup-plugin-copy": "^3.4.0",
      "rollup-plugin-node-resolve": "^5.2.0",
      "rollup-plugin-postcss": "^4.0.0",
      "rollup-plugin-replace": "^2.2.0",
      "rollup-plugin-serve": "^1.1.0",
      "rollup-plugin-terser": "7.0.2",
      "rollup-plugin-vue": "^5.0.0",
      "tslib": "^2.2.0",
      "typescript": "^4.2.4",
      "vue-template-compiler": "^2.6.12"
    },
    "browser": {
      "crypto": false,
      "stream": false
    }
  }
  

after googling I've found that with previous version of crypto it's supposed to work

https://stackoverflow.com/questions/54162297/module-not-found-error-cant-resolve-crypto

brix/crypto-js#291

Any ideas how to fix it in my case?

PKCE.exchangeForAccessToken and error response

The PKCE.exchangeForAccessToken method is typed to return Promise<ITokenResponse> where ITokenResponse is typed to be the following:

export default interface ITokenResponse {
    access_token: string;
    expires_in: number;
    refresh_expires_in: number;
    refresh_token: string;
    scope: string;
    token_type: string;
}

This is incorrect since the token endpoint can return error response as well: https://www.rfc-editor.org/rfc/rfc6749#section-5.2

Based on my testing it seems that the PKCE.exchangeForAccessToken function just returns the error response such as {"error": "invalid_grant"} which is not what the types suggest.

Suggestion:
Either make the function return union of these types or then throw an exception when an error is returned from the authentication server.

How can I get CSRF before exchangeForAccessToken?

Hi,

It's not really an issue with js-pkce code, more like a question about implementation.

I'm using Laravel with Laravel Passport as my backend. /oauth/token is secured by CSRF token check, so when I do exchangeForAccessToken I need to send CSRF token with the request, otherwise it will be rejected. So the question is: how do I get CSRF token after I'm logged in and redirected back from backend's login screen to my frontend app? I think CSRF token is sent back in a cookie, so maybe I could re-use it, but then how do I access it? document.cookie is empty.

Support for custom params

Would be good to be able to send custom query params on the calls to the auth server. Some servers like PingFederate support the concept of authorization adapters, which should be set by the OAuth client.

not getting email data from requested scopes

Hi, i am trying to obtain the access token from the ping federated Server, and also i am successfully able to get it(thanks to js-pkce).The only concern is while im trying to decode the access token over JWT.io , i am getting payload data as follows:
{
"scope": "openid profile address email phone",
"client_id": "Reg1ABCD",
"standardATM": "90fSGwCxckjnKH473",
"iss": "https://something.abc.com",
"acr": "MS-AD-Kerberos",
"sub": "someting123",
"msid": "someting123",
"exp": 1645704086
}

I am expecting my email id under "scope" section, any help on it would be appreciated ,Thanks!!.

Add configuration to allow sending credentials

Currently the fetch POST request for a token sets credentials to omit. But this breaks when using PKCE across domains when the remote server has correctly configured Allow-Origins specified.

I have done some testing in the browser, and I can see the fetch fails because the remote server can't read the cookies it has created for that user during the authorization step. If you simply go into the console and right-click the failed request and select Copy -> Copy Fetch Request, and paste it into the console, and change credentials to include rather than omit, it works as expected.

Would you consider extending this library to allow correctly configured CORS implementaions? Thanks!

Unique code_challenge per request

Our requirements are that every request must use a unique code_challenge within a server to server connection. So in the mean time sessionstorage or localstorage arn't available.

Are there any chance setting a code verifier per user/request? The storage keys are fixed, too.

getting JSON parse error while running npm audit

Hi , i have implemented single sign on in my application using js-pkce library.The code works fine for me. But i tried running npm audit over the application just to check for any vulnerabilities in the library. Here is the error which i encountered on npm audit :

npm ERR! Unexpected end of JSON input while parsing near ''

npm ERR! A complete log of this run can be found in:
npm ERR! C:\Users\ssriv112\AppData\Roaming\npm-cache_logs\2022-02-16T11_47_24_273Z-debug.log

and the log file says that these are the target files having errors :
verbose stack SyntaxError: Unexpected end of JSON input while parsing near ''
**- verbose stack at JSON.parse ()

  • verbose stack at parseJson (C:\Program Files\nodejs\node_modules\npm\node_modules\json-parse-better-errors\index.js:7:17)
  • verbose stack at C:\Program Files\nodejs\node_modules\npm\node_modules\node-fetch-npm\src\body.js:96:50
  • verbose stack at processTicksAndRejections (internal/process/task_queues.js:93:5)**

It would be great if there are any solutions to handle this issue.Attaching "\json-parse-better-errors\index.js" file for your reference
index.js.txt.

Firefox sessionStorage Issues

Issue originates from PR - #4 from @revir

My Findings:
I tested in firefox, and this is not the case. Are you doing something else with your flow? The sessionStorage data is not persisted across tabs, but it is persisted in the same tab. Are you opening a new window to authorize or something?

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.