├── .github ├── FUNDING.yml ├── workflows │ ├── node.js-eol.yml │ ├── node.js-eol-20190430.yml │ ├── node.js-eol-20140731.yml │ ├── node.js-eol-20230430.yml │ ├── nvm.yml │ ├── nvm-manual.yml │ └── node.js.yml ├── PULL_REQUEST_TEMPLATE.md └── ISSUE_TEMPLATE.md ├── etc ├── jsdoc.json └── stunnel.conf ├── support └── mk │ ├── jshint.mk │ ├── notes.mk │ ├── node.mk │ ├── testling.mk │ ├── coveralls.mk │ ├── vows.mk │ ├── browserify.mk │ ├── mocha.mk │ └── istanbul.mk ├── .gitignore ├── test ├── bootstrap │ └── node.js ├── package.test.js ├── verify.test.js └── strategy.test.js ├── Makefile ├── .npmignore ├── .jshintrc ├── CONTRIBUTING.md ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── lib ├── index.js └── strategy.js ├── package.json └── README.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: jaredhanson 2 | -------------------------------------------------------------------------------- /etc/jsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["plugins/markdown"] 3 | } 4 | -------------------------------------------------------------------------------- /etc/stunnel.conf: -------------------------------------------------------------------------------- 1 | pid = 2 | 3 | [proxy] 4 | client = yes 5 | accept = 8080 6 | connect = registry.npmjs.org:443 7 | -------------------------------------------------------------------------------- /support/mk/jshint.mk: -------------------------------------------------------------------------------- 1 | JSHINT ?= jshint 2 | 3 | lint-jshint: 4 | $(JSHINT) $(SOURCES) 5 | 6 | 7 | .PHONY: lint-jshint 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | reports 3 | 4 | # Mac OS X 5 | .DS_Store 6 | 7 | # Node.js 8 | node_modules 9 | npm-debug.log 10 | -------------------------------------------------------------------------------- /support/mk/notes.mk: -------------------------------------------------------------------------------- 1 | NOTES ?= 'TODO|FIXME' 2 | 3 | notes: 4 | grep -Ern $(NOTES) $(SOURCES) $(TESTS) 5 | 6 | 7 | .PHONY: notes 8 | -------------------------------------------------------------------------------- /support/mk/node.mk: -------------------------------------------------------------------------------- 1 | node_modules: 2 | npm install 3 | 4 | clobber-node: 5 | rm -rf node_modules 6 | 7 | 8 | .PHONY: clobber-node 9 | -------------------------------------------------------------------------------- /support/mk/testling.mk: -------------------------------------------------------------------------------- 1 | TESTLING ?= testling 2 | 3 | test-testling: node_modules 4 | $(TESTLING) 5 | 6 | 7 | .PHONY: test-testling 8 | -------------------------------------------------------------------------------- /test/bootstrap/node.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai') 2 | , passport = require('chai-passport-strategy'); 3 | 4 | chai.use(passport); 5 | 6 | 7 | global.expect = chai.expect; 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include node_modules/make-node/main.mk 2 | 3 | MOCHAFLAGS = --require ./test/bootstrap/node 4 | JSDOCFLAGS ?= -c etc/jsdoc.json 5 | 6 | 7 | # Perform self-tests. 8 | check: test 9 | -------------------------------------------------------------------------------- /support/mk/coveralls.mk: -------------------------------------------------------------------------------- 1 | COVERALLS ?= coveralls 2 | 3 | submit-istanbul-lcov-to-coveralls: 4 | cat $(ISTANBUL_LCOV_INFO_PATH) | $(COVERALLS) 5 | 6 | 7 | .PHONY: submit-istanbul-lcov-to-coveralls 8 | -------------------------------------------------------------------------------- /support/mk/vows.mk: -------------------------------------------------------------------------------- 1 | VOWS ?= ./node_modules/.bin/vows 2 | VOWS_REPORTER ?= --spec 3 | 4 | test-vows: node_modules 5 | NODE_PATH=$(NODE_PATH_TEST) \ 6 | $(VOWS) $(VOWS_REPORTER) $(TESTS) 7 | 8 | 9 | .PHONY: test-vows 10 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | README.md 2 | Makefile 3 | build/ 4 | docs/ 5 | examples/ 6 | reports/ 7 | support/ 8 | test/ 9 | 10 | # Mac OS X 11 | .DS_Store 12 | 13 | # Node.js 14 | .npmignore 15 | node_modules/ 16 | npm-debug.log 17 | 18 | # Git 19 | .git* 20 | -------------------------------------------------------------------------------- /support/mk/browserify.mk: -------------------------------------------------------------------------------- 1 | BROWSERIFY ?= browserify 2 | BROWSERIFY_MAIN ?= index.js 3 | BROWSERIFY_OUT ?= build/bundle.js 4 | 5 | build-browserify: node_modules 6 | mkdir -p build 7 | $(BROWSERIFY) $(BROWSERIFY_MAIN) -o $(BROWSERIFY_OUT) 8 | 9 | 10 | .PHONY: build-browserify 11 | -------------------------------------------------------------------------------- /support/mk/mocha.mk: -------------------------------------------------------------------------------- 1 | MOCHA ?= ./node_modules/.bin/mocha 2 | _MOCHA ?= ./node_modules/.bin/_mocha 3 | MOCHA_REPORTER ?= spec 4 | MOCHA_REQUIRE ?= ./test/bootstrap/node 5 | 6 | test-mocha: node_modules 7 | NODE_PATH=$(NODE_PATH_TEST) \ 8 | $(MOCHA) \ 9 | --reporter $(MOCHA_REPORTER) \ 10 | --require $(MOCHA_REQUIRE) $(TESTS) 11 | 12 | 13 | .PHONY: test-mocha 14 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | 4 | "bitwise": true, 5 | "camelcase": true, 6 | "curly": true, 7 | "forin": true, 8 | "immed": true, 9 | "latedef": true, 10 | "newcap": true, 11 | "noarg": true, 12 | "noempty": true, 13 | "nonew": true, 14 | "quotmark": "single", 15 | "undef": true, 16 | "unused": true, 17 | "trailing": true, 18 | 19 | "laxcomma": true 20 | } 21 | -------------------------------------------------------------------------------- /test/package.test.js: -------------------------------------------------------------------------------- 1 | var pkg = require('..'); 2 | 3 | describe('passport-http-bearer', function() { 4 | 5 | it('should export Strategy constructor as module', function() { 6 | expect(pkg).to.be.a('function'); 7 | expect(pkg).to.equal(pkg.Strategy); 8 | }); 9 | 10 | it('should export Strategy constructor', function() { 11 | expect(pkg.Strategy).to.be.a('function'); 12 | }); 13 | 14 | }); 15 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | ### Tests 4 | 5 | The test suite is located in the `test/` directory. All new features are 6 | expected to have corresponding test cases with complete code coverage. Patches 7 | that increase test coverage are happily accepted. 8 | 9 | Ensure that the test suite passes by executing: 10 | 11 | ```bash 12 | $ make test 13 | ``` 14 | 15 | Coverage reports can be generated and viewed by executing: 16 | 17 | ```bash 18 | $ make test-cov 19 | $ make view-cov 20 | ``` 21 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: "node_js" 2 | node_js: 3 | - "11" 4 | - "10" 5 | - "9" 6 | - "8" 7 | - "7" 8 | - "6" 9 | - "5" 10 | - "4" 11 | - "3" # io.js 12 | - "2" # io.js 13 | - "1" # io.js 14 | - "0.12" 15 | - "0.10" 16 | - "0.8" 17 | 18 | 19 | # NOTE: `istanbul` and `coveralls` are pinned for compatibility with node 0.8. 20 | before_install: 21 | - "npm install -g istanbul@0.2.2" 22 | - "npm install -g coveralls@2.11.4" 23 | 24 | script: 25 | - "make check" 26 | 27 | after_success: 28 | - "make report-cov" 29 | 30 | sudo: false 31 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | ### Changed 9 | - Error message when constructed without a verify function changed to use 10 | "function" terminology, rather than "callback", in line with current Passport 11 | best practices. 12 | 13 | ## [1.0.1] - 2013-08-02 14 | 15 | ## [1.0.0] - 2013-08-01 16 | 17 | [Unreleased]: https://github.com/jaredhanson/passport-http-bearer/compare/v1.0.1...HEAD 18 | [1.0.1]: https://github.com/jaredhanson/passport-http-bearer/compare/v1.0.0...v1.0.1 19 | [1.0.0]: https://github.com/jaredhanson/passport-http-bearer/compare/v0.2.1...v1.0.0 20 | -------------------------------------------------------------------------------- /support/mk/istanbul.mk: -------------------------------------------------------------------------------- 1 | ISTANBUL ?= istanbul 2 | ISTANBUL_OUT ?= ./reports/coverage 3 | ISTANBUL_REPORT ?= lcov 4 | ISTANBUL_HTML_REPORT_PATH ?= $(ISTANBUL_OUT)/lcov-report/index.html 5 | ISTANBUL_LCOV_INFO_PATH ?= $(ISTANBUL_OUT)/lcov.info 6 | 7 | 8 | test-istanbul-mocha: node_modules 9 | NODE_PATH=$(NODE_PATH_TEST) \ 10 | $(ISTANBUL) cover \ 11 | --dir $(ISTANBUL_OUT) --report $(ISTANBUL_REPORT) \ 12 | $(_MOCHA) -- \ 13 | --reporter $(MOCHA_REPORTER) \ 14 | --require $(MOCHA_REQUIRE) $(TESTS) 15 | 16 | test-istanbul-vows: node_modules 17 | NODE_PATH=$(NODE_PATH_TEST) \ 18 | $(ISTANBUL) cover \ 19 | --dir $(ISTANBUL_OUT) --report $(ISTANBUL_REPORT) \ 20 | $(VOWS) $(VOWS_REPORTER) $(TESTS) 21 | 22 | view-istanbul-report: 23 | open $(ISTANBUL_HTML_REPORT_PATH) 24 | 25 | 26 | .PHONY: test-istanbul-mocha view-istanbul-report 27 | -------------------------------------------------------------------------------- /.github/workflows/node.js-eol.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs 3 | 4 | name: Node.js CI (EOL) 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [ 16.x ] 20 | 21 | steps: 22 | - uses: actions/checkout@v4 23 | - name: Use Node.js ${{ matrix.node-version }} 24 | uses: actions/setup-node@v4 25 | with: 26 | node-version: ${{ matrix.node-version }} 27 | - run: npm ci 28 | - run: npm test 29 | -------------------------------------------------------------------------------- /.github/workflows/node.js-eol-20190430.yml: -------------------------------------------------------------------------------- 1 | # This workflow performs continuous integration on node 8.x - 14.x, the last of 2 | # which reached end-of-life on 2023-04-30 according to the [release schedule][1] 3 | # published by the [release working group][2]. 4 | # 5 | # [1]: https://github.com/nodejs/release/blob/main/schedule.json 6 | # [2]: https://github.com/nodejs/release 7 | 8 | name: "Node.js CI (EOL: 2019-04-30)" 9 | 10 | on: 11 | push: 12 | branches: [ master ] 13 | pull_request: 14 | branches: [ master ] 15 | 16 | jobs: 17 | build: 18 | 19 | runs-on: ubuntu-20.04 20 | 21 | strategy: 22 | matrix: 23 | node-version: [ 6.x, 4.x, 0.12.x, 0.10.x ] 24 | 25 | # https://blog.npmjs.org/post/171556855892/introducing-npm-ci-for-faster-more-reliable 26 | steps: 27 | - uses: actions/checkout@v4 28 | - name: Use Node.js ${{ matrix.node-version }} 29 | uses: actions/setup-node@v4 30 | with: 31 | node-version: ${{ matrix.node-version }} 32 | - run: npm install 33 | - run: npm test 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2011-2013 Jared Hanson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /.github/workflows/node.js-eol-20140731.yml: -------------------------------------------------------------------------------- 1 | # This workflow performs continuous integration on node 8.x - 14.x, the last of 2 | # which reached end-of-life on 2023-04-30 according to the [release schedule][1] 3 | # published by the [release working group][2]. 4 | # 5 | # [1]: https://github.com/nodejs/release/blob/main/schedule.json 6 | # [2]: https://github.com/nodejs/release 7 | 8 | name: "Node.js CI (EOL: 2014-07-31)" 9 | 10 | on: 11 | push: 12 | branches: [ master ] 13 | pull_request: 14 | branches: [ master ] 15 | 16 | jobs: 17 | build: 18 | 19 | runs-on: ubuntu-latest 20 | 21 | strategy: 22 | matrix: 23 | node-version: [ 0.8.x ] 24 | 25 | # https://github.com/npm/npm/issues/20191 26 | # https://github.blog/security/supply-chain-security/npm-registry-deprecating-tls-1-0-tls-1-1/ 27 | steps: 28 | - uses: actions/checkout@v4 29 | - name: Use Node.js ${{ matrix.node-version }} 30 | uses: actions/setup-node@v4 31 | with: 32 | node-version: ${{ matrix.node-version }} 33 | - run: sudo apt-get install -y stunnel 34 | - run: stunnel etc/stunnel.conf 35 | - run: npm config set registry="http://registry.npmjs.org/" 36 | - run: npm config set proxy http://localhost:8080 37 | - run: npm install 38 | - run: npm test 39 | -------------------------------------------------------------------------------- /.github/workflows/node.js-eol-20230430.yml: -------------------------------------------------------------------------------- 1 | # This workflow performs continuous integration on node 8.x - 14.x, the last of 2 | # which reached end-of-life on 2023-04-30 according to the [release schedule][1] 3 | # published by the [release working group][2]. 4 | # 5 | # [1]: https://github.com/nodejs/release/blob/main/schedule.json 6 | # [2]: https://github.com/nodejs/release 7 | 8 | name: "Node.js CI (EOL: 2023-04-30)" 9 | 10 | on: 11 | push: 12 | branches: [ master ] 13 | pull_request: 14 | branches: [ master ] 15 | 16 | jobs: 17 | build: 18 | 19 | runs-on: ubuntu-latest 20 | 21 | strategy: 22 | matrix: 23 | # The package-lock.json use version 3 of the file format, which is 24 | # [incompatible][1] with npm 6.x (distibuted with node 8.x - 14.x). 25 | # 26 | # [1]: https://docs.npmjs.com/cli/v9/configuring-npm/package-lock-json#lockfileversion 27 | node-version: [ 14.x, 12.x, 10.x, 8.x ] 28 | 29 | steps: 30 | - uses: actions/checkout@v4 31 | - name: Use Node.js ${{ matrix.node-version }} 32 | uses: actions/setup-node@v4 33 | with: 34 | node-version: ${{ matrix.node-version }} 35 | # Ideally, `npm ci` would be run. However, that command [fails][1] because 36 | # lockfileVersion@3 is in use. `npm install` is run as a workaround. 37 | # 38 | # [1]: https://stackoverflow.com/questions/76253884/npm-ci-command-failing-with-cannot-read-property-angular-animations-of-undef 39 | - run: npm install 40 | - run: npm test 41 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The `passport-http-bearer` module provides a {@link https://www.passportjs.org/ Passport} 3 | * strategy for authenticating {@link https://www.passportjs.org/concepts/bearer-token/ bearer tokens} 4 | * used in accordance with the HTTP Bearer authentication scheme. 5 | * 6 | * Bearer tokens are a credential which can be used by any party in possession 7 | * of the token to gain access to a protected resource. Use of a bearer token 8 | * does not require any additional credentials, such as a cryptographic key. As 9 | * such, bearer tokens must be protected from disclosure in both storage and 10 | * transport in order to be utilized securely. 11 | * 12 | * The Bearer authentication scheme is specified by {@link https://www.rfc-editor.org/rfc/rfc6750 RFC 6750}. 13 | * This scheme was designed for use with access tokens issued using {@link https://www.passportjs.org/concepts/oauth2/ OAuth 2.0} 14 | * ({@link https://www.rfc-editor.org/rfc/rfc6749 RFC 6749}). However, this 15 | * scheme is useable within the general HTTP Authentication framework ({@link https://www.rfc-editor.org/rfc/rfc7235 RFC 7235}) 16 | * and can be utilized to authenticate bearer tokens issued via other mechanisms 17 | * as well. 18 | * 19 | * @module passport-http-bearer 20 | */ 21 | 22 | 23 | // Module dependencies. 24 | var Strategy = require('./strategy'); 25 | 26 | /* 27 | * `{@link Strategy}` constructor. 28 | * 29 | * @type {function} 30 | */ 31 | exports = module.exports = Strategy; 32 | 33 | /* 34 | * `{@link Strategy}` constructor. 35 | * 36 | * @type {function} 37 | */ 38 | exports.Strategy = Strategy; 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "passport-http-bearer", 3 | "version": "1.0.1", 4 | "description": "HTTP Bearer authentication strategy for Passport.", 5 | "keywords": [ 6 | "passport", 7 | "auth", 8 | "authn", 9 | "authentication", 10 | "authz", 11 | "authorization", 12 | "http", 13 | "bearer", 14 | "token", 15 | "oauth" 16 | ], 17 | "author": { 18 | "name": "Jared Hanson", 19 | "email": "jaredhanson@gmail.com", 20 | "url": "https://www.jaredhanson.me/" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "git://github.com/jaredhanson/passport-http-bearer.git" 25 | }, 26 | "bugs": { 27 | "url": "https://github.com/jaredhanson/passport-http-bearer/issues" 28 | }, 29 | "funding": { 30 | "type": "github", 31 | "url": "https://github.com/sponsors/jaredhanson" 32 | }, 33 | "license": "MIT", 34 | "licenses": [ 35 | { 36 | "type": "MIT", 37 | "url": "https://opensource.org/licenses/MIT" 38 | } 39 | ], 40 | "main": "./lib", 41 | "dependencies": { 42 | "passport-strategy": "1.x.x" 43 | }, 44 | "devDependencies": { 45 | "make-node": "0.4.6", 46 | "mocha": "2.x.x", 47 | "chai": "2.x.x", 48 | "chai-passport-strategy": "3.x.x" 49 | }, 50 | "engines": { 51 | "node": ">= 0.4.0" 52 | }, 53 | "scripts": { 54 | "test": "mocha --require ./test/bootstrap/node --recursive" 55 | }, 56 | "testling": { 57 | "browsers": [ 58 | "chrome/latest" 59 | ], 60 | "harness": "mocha", 61 | "files": [ 62 | "test/bootstrap/testling.js", 63 | "test/*.test.js" 64 | ] 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /.github/workflows/nvm.yml: -------------------------------------------------------------------------------- 1 | name: Node.js NVM 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | env: 14 | ADDITIONAL_PARAMETERS: --without-snapshot 15 | CFLAGS: -O2 16 | CXXFLAGS: -O2 17 | 18 | strategy: 19 | matrix: 20 | # node-version: [ '0.8.x', '0.6.x' ] 21 | node-version: [ '0.8.x' ] 22 | 23 | steps: 24 | - run: sudo apt-get install -y python2.7 25 | - run: ls /usr/bin/p* 26 | - run: ls -la /usr/bin/python 27 | - run: sudo ln -fs /usr/bin/python2.7 /usr/bin/python 28 | - run: ls -la /usr/bin/python 29 | - run: python --version 30 | - run: ulimit -v unlimited 31 | #- run: export PYTHON=/usr/bin/python2.7 32 | - uses: actions/checkout@v4 33 | #- run: export CFLAGS=-O2 34 | #- run: export CXXFLAGS=-O2 35 | - run: echo $CFLAGS 36 | - run: echo $CXXFLAGS 37 | #- run: export ADDITIONAL_PARAMETERS=--without-snapshot 38 | #- run: export ADDITIONAL_PARAMETERS=CFLAGS='-O2' CXXFLAGS='-O2' 39 | - run: echo $ADDITIONAL_PARAMETERS 40 | - name: Use Node.js ${{ matrix.node-version }} 41 | uses: dcodeIO/setup-node-nvm@v5 42 | with: 43 | node-version: ${{ matrix.node-version }} 44 | node-mirror: https://nodejs.org/dist 45 | - run: sudo apt-get install -y stunnel 46 | - run: stunnel etc/stunnel.conf 47 | - run: npm config set registry="http://registry.npmjs.org/" 48 | - run: npm config set proxy http://localhost:8080 49 | - run: npm install 50 | - run: npm test 51 | #- run: which nvm 52 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ** READ THIS FIRST! ** 2 | 3 | #### Are you implementing a new feature? 4 | 5 | Requests for new features should first be discussed on the [developer forum](https://github.com/passport/develop). 6 | This allows the community to gather feedback and assess whether or not there is 7 | an existing way to achieve the desired functionality. 8 | 9 | If it is determined that a new feature needs to be implemented, include a link 10 | to the relevant discussion along with the pull request. 11 | 12 | #### Is this a security patch? 13 | 14 | Do not open pull requests that might have security implications. Potential 15 | security vulnerabilities should be reported privately to jaredhanson@gmail.com. 16 | Once any vulerabilities have been repaired, the details will be disclosed 17 | publicly in a responsible manner. This also allows time for coordinating with 18 | affected parties in order to mitigate negative consequences. 19 | 20 | 21 | If neither of the above two scenarios apply to your situation, you should open 22 | a pull request. Delete this paragraph and the text above, and fill in the 23 | information requested below. 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | ### Checklist 33 | 34 | 35 | 36 | 37 | - [ ] I have read the [CONTRIBUTING](https://github.com/jaredhanson/passport-http-bearer/blob/master/CONTRIBUTING.md) guidelines. 38 | - [ ] I have added test cases which verify the correct operation of this feature or patch. 39 | - [ ] I have added documentation pertaining to this feature or patch. 40 | - [ ] The automated test suite (`$ make test`) executes successfully. 41 | - [ ] The automated code linting (`$ make lint`) executes successfully. 42 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ** READ THIS FIRST! ** 2 | 3 | #### Are you looking for help? 4 | 5 | Reminder: The issue tracker is not a support forum. 6 | 7 | Issues should only be filed in this project once they are able to be reproduced 8 | and confirmed as a flaw in the software or incorrect information in associated 9 | documention. 10 | 11 | If you are encountering problems integrating this module into your application, 12 | please post a question on the [discussion forum](https://github.com/passport/discuss) 13 | rather than filing an issue. 14 | 15 | #### Is this a security issue? 16 | 17 | Do not open issues that might have security implications. Potential security 18 | vulnerabilities should be reported privately to jaredhanson@gmail.com. Once any 19 | vulerabilities have been repaired, the details will be disclosed publicly in a 20 | responsible manner. This also allows time for coordinating with affected parties 21 | in order to mitigate negative consequences. 22 | 23 | 24 | If neither of the above two scenarios apply to your situation, you should open 25 | an issue. Delete this paragraph and the text above, and fill in the information 26 | requested below. 27 | 28 | 29 | 30 | 31 | 32 | 33 | ### Expected behavior 34 | 35 | 36 | 37 | ### Actual behavior 38 | 39 | 40 | 41 | ### Steps to reproduce 42 | 43 | 44 | 45 | ```js 46 | // Format code using Markdown code blocks 47 | ``` 48 | 49 | ### Environment 50 | 51 | * Operating System: 52 | * Node version: 53 | * passport version: 54 | * passport-http-bearer version: 55 | -------------------------------------------------------------------------------- /.github/workflows/nvm-manual.yml: -------------------------------------------------------------------------------- 1 | name: Node.js NVM Manual 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | env: 14 | NVM_NODEJS_ORG_MIRROR: https://nodejs.org/dist 15 | # ADDITIONAL_PARAMETERS: --without-snapshot 16 | # CFLAGS: -O2 17 | # CXXFLAGS: -I/foo/openssl 18 | # CXXFLAGS: -I/home/runner/.nvm/.cache/src/node-v0.6.21/files/deps/openssl/include 19 | 20 | # https://github.com/nodejs/node-gyp/blob/main/docs/Linking-to-OpenSSL.md 21 | strategy: 22 | matrix: 23 | # node-version: [ '0.8.x', '0.6.x' ] 24 | node-version: [ '0.6.x' ] 25 | 26 | # https://groups.google.com/g/mailing.openssl.users/c/Qi5yYi8ZzPo 27 | # https://github.com/openssl/openssl/blob/master/INSTALL.md 28 | steps: 29 | - run: echo $PWD 30 | - run: ls 31 | - run: ls /usr/local 32 | - run: wget https://www.openssl.org/source/openssl-0.9.8r.tar.gz 33 | - run: ls 34 | - run: tar xvzf openssl-0.9.8r.tar.gz 35 | - name: Build OpenSSL 36 | working-directory: ./openssl-0.9.8r 37 | run: | 38 | ./config 39 | make 40 | sudo make install_sw 41 | #- run: apt list -a libssl-dev 42 | - run: ls /usr/local 43 | - run: ls /usr/local/ssl 44 | - run: ls /usr/local/ssl/lib 45 | - run: sudo apt-get update 46 | #- run: sudo apt-get install libssl1.1 47 | - run: sudo apt-get install -y python2.7 48 | #- run: sudo apt-get install -y libssl-dev 49 | #- run: ls /usr/include 50 | #- run: ls /usr/bin/p* 51 | #- run: ls -la /usr/bin/python 52 | - run: sudo ln -fs /usr/bin/python2.7 /usr/bin/python 53 | #- run: ls -la /usr/bin/python 54 | #- run: python --version 55 | - uses: actions/checkout@v4 56 | - run: echo $PWD 57 | - run: curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash 58 | - run: . $HOME/.nvm/nvm.sh && nvm install 0.6 --without-snapshot --openssl-includes=/usr/local/ssl/include --openssl-libpath=/usr/local/ssl/lib 59 | #- run: . $HOME/.nvm/nvm.sh && nvm install 0.6 --without-snapshot --without-ssl 60 | #- run: which nvm 61 | - run: npm install 62 | - run: npm test 63 | 64 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs 3 | # https://github.com/actions/starter-workflows/blob/main/ci/node.js.yml 4 | 5 | name: Node.js CI 6 | 7 | on: 8 | push: 9 | branches: [ master ] 10 | pull_request: 11 | branches: [ master ] 12 | 13 | jobs: 14 | build: 15 | 16 | runs-on: ubuntu-latest 17 | 18 | strategy: 19 | matrix: 20 | node-version: 21 | - current 22 | - 22.x 23 | - 20.x 24 | - 18.x 25 | - 16.x 26 | # - 14.x 27 | - 8.x 28 | - 6.x 29 | 30 | steps: 31 | - uses: actions/checkout@v4 32 | 33 | - id: setup-node 34 | name: Use Node.js ${{ matrix.node-version }} 35 | uses: actions/setup-node@v4 36 | with: 37 | node-version: ${{ matrix.node-version }} 38 | 39 | - id: node-v 40 | name: Output Node.js version 41 | run: echo "version=$(node -v)" >> $GITHUB_OUTPUT 42 | 43 | - id: node-version 44 | name: Parse Node.js version 45 | uses: apexskier/github-semver-parse@v1 46 | with: 47 | version: ${{ steps.node-v.outputs.version }} 48 | 49 | - run: which jq 50 | 51 | - env: 52 | N_MAJOR: ${{steps.node-version.outputs.major}} 53 | N_MINOR: ${{steps.node-version.outputs.minor}} 54 | run: echo "node - $N_MAJOR $N_MINOR" 55 | 56 | - id: npm-v 57 | name: Output npm version 58 | run: echo "version=$(npm -v)" >> $GITHUB_OUTPUT 59 | 60 | - id: npm-version 61 | name: Parse npm version 62 | uses: apexskier/github-semver-parse@v1 63 | with: 64 | version: ${{ steps.npm-v.outputs.version }} 65 | 66 | - env: 67 | N_MAJOR: ${{steps.npm-version.outputs.major}} 68 | N_MINOR: ${{steps.npm-version.outputs.minor}} 69 | run: echo "npm - $N_MAJOR $N_MINOR" 70 | 71 | - id: lockfile-version 72 | name: Get package-lock.json lockfileVersion 73 | run: echo "version=$(cat package-lock.json | jq '.lockfileVersion')" >> $GITHUB_OUTPUT 74 | 75 | - env: 76 | N_MAJOR: ${{steps.lockfile-version.outputs.version}} 77 | run: echo "lockfile - $N_MAJOR" 78 | 79 | - run: npm ci 80 | # The [`ci`][1] command was [introduced][2] with npm 6.x, and is intended 81 | # to be used in continuous integration environments. If npm 6.x or later 82 | # is available, `npm ci` is executed to install dependencies. Otherwise, 83 | # `npm install` is executed. 84 | # 85 | # [1]: https://docs.npmjs.com/cli/v10/commands/npm-ci 86 | # [2]: https://blog.npmjs.org/post/171556855892/introducing-npm-ci-for-faster-more-reliable 87 | if: steps.npm-version.outputs.major >= 6 88 | 89 | - run: npm install 90 | if: steps.npm-version.outputs.major < 6 91 | 92 | - run: npm test 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # passport-http-bearer 2 | 3 | HTTP Bearer authentication strategy for [Passport](https://www.passportjs.org/). 4 | 5 | This module lets you authenticate HTTP requests using [bearer tokens](https://www.passportjs.org/concepts/bearer-token/), 6 | as specified by [RFC 6750](https://www.rfc-editor.org/rfc/rfc6750), in your 7 | Node.js applications. By plugging into Passport, bearer token support can be 8 | easily and unobtrusively integrated into any application or framework that 9 | supports [Connect](https://github.com/senchalabs/connect#readme)-style 10 | middleware, including [Express](https://expressjs.com/). 11 | 12 |
13 | 14 | :hammer_and_wrench: [API Reference](https://www.passportjs.org/api/passport-http-bearer/1.x/?utm_source=github&utm_medium=referral&utm_campaign=passport-http-bearer&utm_content=nav-api) • 15 | :heart: [Sponsors](https://www.passportjs.org/sponsors/?utm_source=github&utm_medium=referral&utm_campaign=passport-http-bearer&utm_content=nav-sponsors) 16 | 17 |
18 | 19 | --- 20 | 21 |

22 | Advertisement 23 |
24 | Node.js, Express, MongoDB & More: The Complete Bootcamp 2020
Master Node by building a real-world RESTful API and web app (with authentication, Node.js security, payments & more) 25 |

26 | 27 | --- 28 | 29 | [![npm](https://img.shields.io/npm/v/passport-http-bearer.svg)](https://www.npmjs.com/package/passport-http-bearer) 30 | [![build](https://img.shields.io/travis/jaredhanson/passport-http-bearer.svg)](https://travis-ci.org/jaredhanson/passport-http-bearer) 31 | [![coverage](https://img.shields.io/coveralls/jaredhanson/passport-http-bearer.svg)](https://coveralls.io/github/jaredhanson/passport-http-bearer) 32 | [...](https://github.com/jaredhanson/passport-http-bearer/wiki/Status) 33 | 34 | ## Install 35 | 36 | $ npm install passport-http-bearer 37 | 38 | #### TypeScript support 39 | 40 | ```bash 41 | $ npm install @types/passport-http-bearer 42 | ``` 43 | 44 | ## Usage 45 | 46 | #### Configure Strategy 47 | 48 | The HTTP Bearer authentication strategy authenticates users using a bearer 49 | token. The strategy requires a `verify` callback, which accepts that 50 | credential and calls `done` providing a user. Optional `info` can be passed, 51 | typically including associated scope, which will be set by Passport at 52 | `req.authInfo` to be used by later middleware for authorization and access 53 | control. 54 | 55 | ```js 56 | passport.use(new BearerStrategy( 57 | function(token, done) { 58 | User.findOne({ token: token }, function (err, user) { 59 | if (err) { return done(err); } 60 | if (!user) { return done(null, false); } 61 | return done(null, user, { scope: 'all' }); 62 | }); 63 | } 64 | )); 65 | ``` 66 | 67 | #### Authenticate Requests 68 | 69 | Use `passport.authenticate()`, specifying the `'bearer'` strategy, to 70 | authenticate requests. Requests containing bearer tokens do not require session 71 | support, so the `session` option can be set to `false`. 72 | 73 | For example, as route middleware in an [Express](http://expressjs.com/) 74 | application: 75 | 76 | ```js 77 | app.get('/profile', 78 | passport.authenticate('bearer', { session: false }), 79 | function(req, res) { 80 | res.json(req.user); 81 | }); 82 | ``` 83 | 84 | #### Issuing Tokens 85 | 86 | Bearer tokens are typically issued using OAuth 2.0. [OAuth2orize](https://github.com/jaredhanson/oauth2orize) 87 | is a toolkit for implementing OAuth 2.0 servers and issuing bearer tokens. Once 88 | issued, this module can be used to authenticate tokens as described above. 89 | 90 | #### Making authenticated requests 91 | The HTTP Bearer authentication strategy authenticates requests based on a bearer token contained in the: 92 | * `Authorization` header field where the value is in the format `{scheme} {token}` and scheme is "Bearer" in this case. 93 | * or `access_token` body parameter 94 | * or `access_token` query parameter 95 | 96 | ## Examples 97 | 98 | For a complete, working example, refer to the [Bearer example](https://github.com/passport/express-4.x-http-bearer-example). 99 | 100 | ## Related Modules 101 | 102 | - [OAuth2orize](https://github.com/jaredhanson/oauth2orize) — OAuth 2.0 authorization server toolkit 103 | 104 | ## License 105 | 106 | [The MIT License](http://opensource.org/licenses/MIT) 107 | 108 | Copyright (c) 2011-2013 Jared Hanson <[https://www.jaredhanson.me/](https://www.jaredhanson.me/)> 109 | -------------------------------------------------------------------------------- /test/verify.test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'); 2 | var Strategy = require('../lib/strategy'); 3 | 4 | 5 | describe('verify function', function() { 6 | 7 | describe('that authenticates', function() { 8 | 9 | it('should authenticate request', function(done) { 10 | var strategy = new Strategy(function(token, cb) { 11 | expect(token).to.equal('mF_9.B5f-4.1JqM'); 12 | return cb(null, { id: '248289761001' }); 13 | }); 14 | 15 | chai.passport.use(strategy) 16 | .request(function(req) { 17 | req.headers['authorization'] = 'Bearer mF_9.B5f-4.1JqM'; 18 | }) 19 | .success(function(user, info) { 20 | expect(user).to.deep.equal({ id: '248289761001' }); 21 | expect(info).to.be.undefined; 22 | done(); 23 | }) 24 | .authenticate(); 25 | }); // should authenticate request 26 | 27 | it('should authenticate request with additional info', function(done) { 28 | var strategy = new Strategy(function(token, cb) { 29 | expect(token).to.equal('mF_9.B5f-4.1JqM'); 30 | return cb(null, { id: '248289761001' }, { scope: [ 'profile', 'email' ] }); 31 | }); 32 | 33 | chai.passport.use(strategy) 34 | .request(function(req) { 35 | req.headers['authorization'] = 'Bearer mF_9.B5f-4.1JqM'; 36 | }) 37 | .success(function(user, info) { 38 | expect(user).to.deep.equal({ id: '248289761001' }); 39 | expect(info).to.deep.equal({ scope: [ 'profile', 'email' ] }); 40 | done(); 41 | }) 42 | .authenticate(); 43 | }); // should authenticate request with additional info 44 | 45 | it('should accept request argument and authenticate request', function(done) { 46 | var strategy = new Strategy({ passReqToCallback: true }, function(req, token, cb) { 47 | expect(req.url).to.equal('/'); 48 | expect(token).to.equal('mF_9.B5f-4.1JqM'); 49 | return cb(null, { id: '248289761001' }, { scope: [ 'profile', 'email' ] }); 50 | }); 51 | 52 | chai.passport.use(strategy) 53 | .request(function(req) { 54 | req.headers['authorization'] = 'Bearer mF_9.B5f-4.1JqM'; 55 | }) 56 | .success(function(user, info) { 57 | expect(user).to.deep.equal({ id: '248289761001' }); 58 | expect(info).to.deep.equal({ scope: [ 'profile', 'email' ] }); 59 | done(); 60 | }) 61 | .authenticate(); 62 | }); // should accept request argument and authenticate request 63 | 64 | }); // that authenticates 65 | 66 | describe('that does not authenticate', function() { 67 | 68 | it('should challenge request', function(done) { 69 | var strategy = new Strategy(function(token, cb) { 70 | return cb(null, false); 71 | }); 72 | 73 | chai.passport.use(strategy) 74 | .request(function(req) { 75 | req.headers['authorization'] = 'Bearer mF_9.B5f-4.1JqM'; 76 | }) 77 | .fail(function(challenge, status) { 78 | expect(challenge).to.equal('Bearer realm="Users", error="invalid_token"'); 79 | expect(status).to.be.undefined; 80 | done(); 81 | }) 82 | .authenticate(); 83 | }); // should challenge request 84 | 85 | it('should challenge request with explanation', function(done) { 86 | var strategy = new Strategy(function(token, cb) { 87 | return cb(null, false, { message: 'The access token expired' }); 88 | }); 89 | 90 | chai.passport.use(strategy) 91 | .request(function(req) { 92 | req.headers['authorization'] = 'Bearer mF_9.B5f-4.1JqM'; 93 | }) 94 | .fail(function(challenge, status) { 95 | expect(challenge).to.equal('Bearer realm="Users", error="invalid_token", error_description="The access token expired"'); 96 | expect(status).to.be.undefined; 97 | done(); 98 | }) 99 | .authenticate(); 100 | }); // should challenge request with explanation 101 | 102 | it('should challenge request with explanation as string', function(done) { 103 | var strategy = new Strategy(function(token, cb) { 104 | return cb(null, false, 'The access token expired'); 105 | }); 106 | 107 | chai.passport.use(strategy) 108 | .request(function(req) { 109 | req.headers['authorization'] = 'Bearer mF_9.B5f-4.1JqM'; 110 | }) 111 | .fail(function(challenge, status) { 112 | expect(challenge).to.equal('Bearer realm="Users", error="invalid_token", error_description="The access token expired"'); 113 | expect(status).to.be.undefined; 114 | done(); 115 | }) 116 | .authenticate(); 117 | }); // should challenge request with explanation as string 118 | 119 | }); // that does not authenticate 120 | 121 | describe('that errors', function() { 122 | 123 | it('should error request', function(done) { 124 | var strategy = new Strategy(function(token, cb) { 125 | return cb(new Error('something went wrong')); 126 | }); 127 | 128 | chai.passport.use(strategy) 129 | .request(function(req) { 130 | req.headers['authorization'] = 'Bearer mF_9.B5f-4.1JqM'; 131 | }) 132 | .error(function(err) { 133 | expect(err).to.be.an.instanceof(Error); 134 | expect(err.message).to.equal('something went wrong'); 135 | done(); 136 | }) 137 | .authenticate(); 138 | }); // should error request 139 | 140 | }); // that errors 141 | 142 | }); 143 | -------------------------------------------------------------------------------- /lib/strategy.js: -------------------------------------------------------------------------------- 1 | // Module dependencies. 2 | var passport = require('passport-strategy') 3 | , util = require('util'); 4 | 5 | 6 | /** 7 | * Create a new `Strategy` object. 8 | * 9 | * @classdesc This `Strategy` authenticates HTTP requests that use the Bearer 10 | * authentication scheme, as specified by {@link https://www.rfc-editor.org/rfc/rfc6750 RFC 6750}. 11 | * 12 | * The bearer token credential can be sent in the HTTP request in one of three 13 | * different ways. Preferably, the token is sent in the "Authorization" header 14 | * field: 15 | * 16 | * ```http 17 | * GET /resource HTTP/1.1 18 | * Host: server.example.com 19 | * Authorization: Bearer mF_9.B5f-4.1JqM 20 | * ``` 21 | * 22 | * Alternatively, the token can be sent in a form-encoded body, using the 23 | * `access_token` parameter: 24 | * 25 | * ```http 26 | * POST /resource HTTP/1.1 27 | * Host: server.example.com 28 | * Content-Type: application/x-www-form-urlencoded 29 | * 30 | * access_token=mF_9.B5f-4.1JqM 31 | * ``` 32 | * 33 | * Or, in the URL, using the `access_token` query parameter: 34 | * 35 | * ```http 36 | * GET /resource?access_token=mF_9.B5f-4.1JqM HTTP/1.1 37 | * Host: server.example.com 38 | * ``` 39 | * 40 | * @public 41 | * @class 42 | * @augments base.Strategy 43 | * @param {Object} [options] 44 | * @param {string} [options.realm='Users'] - Value indicating the protection 45 | * space over which credentials are valid. 46 | * @param {string} [options.scope] - Value indicating required scope needed to 47 | * access protected resources. 48 | * @param {boolean} [options.passReqToCallback=false] - When `true`, the 49 | * `verify` function receives the request object as the first argument, 50 | * in accordance with the `{@link Strategy~verifyWithReqFn}` signature. 51 | * @param {Strategy~verifyFn|Strategy~verifyWithReqFn} verify - Function which 52 | * verifies access token. 53 | * 54 | * @example 55 | * var BearerStrategy = require('passport-http-bearer').Strategy; 56 | * 57 | * new BearerStrategy(function(token, cb) { 58 | * tokens.findOne({ value: token }, function(err, claims) { 59 | * if (err) { return cb(err); } 60 | * if (!claims) { return cb(null, false); } 61 | * 62 | * users.findOne({ id: claims.userID }, function (err, user) { 63 | * if (err) { return cb(err); } 64 | * if (!user) { return cb(null, false); } 65 | * return cb(null, user, { scope: claims.scope }); 66 | * }); 67 | * }); 68 | * }); 69 | */ 70 | function Strategy(options, verify) { 71 | if (typeof options == 'function') { 72 | verify = options; 73 | options = {}; 74 | } 75 | if (!verify) { throw new TypeError('HTTPBearerStrategy requires a verify function'); } 76 | 77 | passport.Strategy.call(this); 78 | 79 | /** The name of the strategy, set to `'bearer'`. 80 | * 81 | * @type {string} 82 | * @readonly 83 | */ 84 | this.name = 'bearer'; 85 | this._verify = verify; 86 | this._realm = options.realm || 'Users'; 87 | if (options.scope) { 88 | this._scope = (Array.isArray(options.scope)) ? options.scope : [ options.scope ]; 89 | } 90 | this._passReqToCallback = options.passReqToCallback; 91 | } 92 | 93 | // Inherit from `passport.Strategy`. 94 | util.inherits(Strategy, passport.Strategy); 95 | 96 | /** 97 | * Authenticate request by verifying access token. 98 | * 99 | * When a bearer token is sent in the request, it will be parsed and the verify 100 | * function will be called to verify the token and authenticate the request. If 101 | * a token is not present, authentication will fail with the appropriate 102 | * challenge and status code. 103 | * 104 | * This function is protected, and should not be called directly. Instead, 105 | * use `passport.authenticate()` middleware and specify the {@link Strategy#name `name`} 106 | * of this strategy and any options. 107 | * 108 | * @protected 109 | * @param {http.IncomingMessage} req - The Node.js {@link https://nodejs.org/api/http.html#class-httpincomingmessage `IncomingMessage`} 110 | * object. 111 | * 112 | * @example 113 | * passport.authenticate('bearer'); 114 | */ 115 | Strategy.prototype.authenticate = function(req) { 116 | var token; 117 | 118 | if (req.headers && req.headers.authorization) { 119 | var parts = req.headers.authorization.split(' '); 120 | if (parts.length == 2) { 121 | var scheme = parts[0] 122 | , credentials = parts[1]; 123 | 124 | if (/^Bearer$/i.test(scheme)) { 125 | token = credentials; 126 | } 127 | } else { 128 | return this.fail(400); 129 | } 130 | } 131 | 132 | if (req.body && req.body.access_token) { 133 | if (token) { return this.fail(400); } 134 | token = req.body.access_token; 135 | } 136 | 137 | if (req.query && req.query.access_token) { 138 | if (token) { return this.fail(400); } 139 | token = req.query.access_token; 140 | } 141 | 142 | if (!token) { return this.fail(this._challenge()); } 143 | 144 | var self = this; 145 | 146 | function verified(err, user, info) { 147 | if (err) { return self.error(err); } 148 | if (!user) { 149 | if (typeof info == 'string') { 150 | info = { message: info } 151 | } 152 | info = info || {}; 153 | return self.fail(self._challenge('invalid_token', info.message)); 154 | } 155 | self.success(user, info); 156 | } 157 | 158 | if (self._passReqToCallback) { 159 | this._verify(req, token, verified); 160 | } else { 161 | this._verify(token, verified); 162 | } 163 | }; 164 | 165 | /** 166 | * Build authentication challenge. 167 | * 168 | * @private 169 | */ 170 | Strategy.prototype._challenge = function(code, desc, uri) { 171 | var challenge = 'Bearer realm="' + this._realm + '"'; 172 | if (this._scope) { 173 | challenge += ', scope="' + this._scope.join(' ') + '"'; 174 | } 175 | if (code) { 176 | challenge += ', error="' + code + '"'; 177 | } 178 | if (desc && desc.length) { 179 | challenge += ', error_description="' + desc + '"'; 180 | } 181 | if (uri && uri.length) { 182 | challenge += ', error_uri="' + uri + '"'; 183 | } 184 | 185 | return challenge; 186 | }; 187 | 188 | // Export `Strategy`. 189 | module.exports = Strategy; 190 | 191 | 192 | /** 193 | * Verifies `token` and yields authenticated user. 194 | * 195 | * This function is called by `{@link Strategy}` to verify an access token, and 196 | * must invoke `cb` to yield the result. 197 | * 198 | * @callback Strategy~verifyFn 199 | * @param {string} token - The access token received in the request. 200 | * @param {function} cb 201 | * @param {?Error} cb.err - An `Error` if an error occured; otherwise `null`. 202 | * @param {Object|boolean} cb.user - An `Object` representing the authenticated 203 | * user if verification was successful; otherwise `false`. 204 | * @param {Object} cb.info - Additional application-specific context that will be 205 | * passed through for further request processing. 206 | */ 207 | 208 | /** 209 | * Verifies `token` and yields authenticated user. 210 | * 211 | * This function is called by `{@link Strategy}` to verify an access token when 212 | * the `passReqToCallback` option is set, and must invoke `cb` to yield the 213 | * result. 214 | * 215 | * @callback Strategy~verifyWithReqFn 216 | * @param {http.IncomingMessage} req - The Node.js {@link https://nodejs.org/api/http.html#class-httpincomingmessage `IncomingMessage`} 217 | * object. 218 | * @param {string} token - The access token received in the request. 219 | * @param {function} cb 220 | * @param {?Error} cb.err - An `Error` if an error occured; otherwise `null`. 221 | * @param {Object|boolean} cb.user - An `Object` representing the authenticated 222 | * user if verification was successful; otherwise `false`. 223 | * @param {Object} cb.info - Additional application-specific context that will be 224 | * passed through for further request processing. 225 | */ 226 | -------------------------------------------------------------------------------- /test/strategy.test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'); 2 | var Strategy = require('../lib/strategy'); 3 | 4 | 5 | describe('Strategy', function() { 6 | 7 | var strategy = new Strategy(function(token, cb) { 8 | throw new Error('verify function should not be called'); 9 | }); 10 | 11 | 12 | it('should be named bearer', function() { 13 | expect(strategy.name).to.equal('bearer'); 14 | }); 15 | 16 | it('should authenticate request with bearer scheme', function(done) { 17 | var strategy = new Strategy(function(token, cb) { 18 | return cb(null, { id: '248289761001' }); 19 | }); 20 | 21 | chai.passport.use(strategy) 22 | .request(function(req) { 23 | req.headers['authorization'] = 'Bearer mF_9.B5f-4.1JqM'; 24 | }) 25 | .success(function(user, info) { 26 | expect(user).to.deep.equal({ id: '248289761001' }); 27 | expect(info).to.be.undefined; 28 | done(); 29 | }) 30 | .authenticate(); 31 | }); // should authenticate request with bearer scheme 32 | 33 | it('should authenticate request with case-insensitive bearer scheme', function(done) { 34 | var strategy = new Strategy(function(token, cb) { 35 | return cb(null, { id: '248289761001' }); 36 | }); 37 | 38 | chai.passport.use(strategy) 39 | .request(function(req) { 40 | req.headers['authorization'] = 'bearer mF_9.B5f-4.1JqM'; 41 | }) 42 | .success(function(user, info) { 43 | expect(user).to.deep.equal({ id: '248289761001' }); 44 | expect(info).to.be.undefined; 45 | done(); 46 | }) 47 | .authenticate(); 48 | }); // should authenticate request with case-insensitive bearer scheme 49 | 50 | it('should authenticate request with token in form-encoded body parameter', function(done) { 51 | var strategy = new Strategy(function(token, cb) { 52 | return cb(null, { id: '248289761001' }); 53 | }); 54 | 55 | chai.passport.use(strategy) 56 | .request(function(req) { 57 | req.body = {}; 58 | req.body.access_token = 'mF_9.B5f-4.1JqM'; 59 | }) 60 | .success(function(user, info) { 61 | expect(user).to.deep.equal({ id: '248289761001' }); 62 | expect(info).to.be.undefined; 63 | done(); 64 | }) 65 | .authenticate(); 66 | }); // should authenticate request with token in form-encoded body parameter 67 | 68 | it('should authenticate request with token in URI query parameter', function(done) { 69 | var strategy = new Strategy(function(token, cb) { 70 | return cb(null, { id: '248289761001' }); 71 | }); 72 | 73 | chai.passport.use(strategy) 74 | .request(function(req) { 75 | req.query = {}; 76 | req.query.access_token = 'mF_9.B5f-4.1JqM'; 77 | }) 78 | .success(function(user, info) { 79 | expect(user).to.deep.equal({ id: '248289761001' }); 80 | expect(info).to.be.undefined; 81 | done(); 82 | }) 83 | .authenticate(); 84 | }); // should authenticate request with token in URI query parameter 85 | 86 | it('should challenge request with realm', function(done) { 87 | var strategy = new Strategy({ realm: 'example' }, function(token, cb) { 88 | throw new Error('verify function should not be called'); 89 | }); 90 | 91 | chai.passport.use(strategy) 92 | .fail(function(challenge, status) { 93 | expect(challenge).to.equal('Bearer realm="example"'); 94 | expect(status).to.be.undefined; 95 | done(); 96 | }) 97 | .authenticate(); 98 | }); 99 | 100 | it('should challenge request with scope as array', function(done) { 101 | var strategy = new Strategy({ scope: ['profile', 'email'] }, function(token, cb) { 102 | throw new Error('verify function should not be called'); 103 | }); 104 | 105 | chai.passport.use(strategy) 106 | .fail(function(challenge, status) { 107 | expect(challenge).to.equal('Bearer realm="Users", scope="profile email"'); 108 | expect(status).to.be.undefined; 109 | done(); 110 | }) 111 | .authenticate(); 112 | }); 113 | 114 | it('should challenge request with scope as string', function(done) { 115 | var strategy = new Strategy({ scope: 'profile' }, function(token, cb) { 116 | throw new Error('verify function should not be called'); 117 | }); 118 | 119 | chai.passport.use(strategy) 120 | .fail(function(challenge, status) { 121 | expect(challenge).to.equal('Bearer realm="Users", scope="profile"'); 122 | expect(status).to.be.undefined; 123 | done(); 124 | }) 125 | .authenticate(); 126 | }); 127 | 128 | it('should challenge request without credentials', function(done) { 129 | chai.passport.use(strategy) 130 | .fail(function(challenge, status) { 131 | expect(challenge).to.equal('Bearer realm="Users"'); 132 | expect(status).to.be.undefined; 133 | done(); 134 | }) 135 | .authenticate(); 136 | }); 137 | 138 | it('should challenge request with non-bearer scheme', function(done) { 139 | chai.passport.use(strategy) 140 | .request(function(req) { 141 | req.headers['authorization'] = 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='; 142 | }) 143 | .fail(function(challenge, status) { 144 | expect(challenge).to.equal('Bearer realm="Users"'); 145 | expect(status).to.be.undefined; 146 | done(); 147 | }) 148 | .authenticate(); 149 | }); 150 | 151 | it('should challenge request with scheme name differing by suffix', function(done) { 152 | chai.passport.use(strategy) 153 | .request(function(req) { 154 | req.headers['authorization'] = 'Bearer2 mF_9.B5f-4.1JqM'; 155 | }) 156 | .fail(function(challenge, status) { 157 | expect(challenge).to.equal('Bearer realm="Users"'); 158 | expect(status).to.be.undefined; 159 | done(); 160 | }) 161 | .authenticate(); 162 | }); 163 | 164 | it('should challenge request with scheme name differing by prefix', function(done) { 165 | chai.passport.use(strategy) 166 | .request(function(req) { 167 | req.headers['authorization'] = 'XBearer mF_9.B5f-4.1JqM'; 168 | }) 169 | .fail(function(challenge, status) { 170 | expect(challenge).to.equal('Bearer realm="Users"'); 171 | expect(status).to.be.undefined; 172 | done(); 173 | }) 174 | .authenticate(); 175 | }); 176 | 177 | it('should refuse request with with bearer scheme that lacks token', function(done) { 178 | chai.passport.use(strategy) 179 | .request(function(req) { 180 | req.headers['authorization'] = 'Bearer'; 181 | }) 182 | .fail(function(status) { 183 | expect(status).to.equal(400); 184 | done(); 185 | }) 186 | .authenticate(); 187 | }); 188 | 189 | it('should refuse request with token transmitted in both header field and form-encoded body parameter', function(done) { 190 | chai.passport.use(strategy) 191 | .request(function(req) { 192 | req.headers['authorization'] = 'Bearer mF_9.B5f-4.1JqM'; 193 | req.body = {}; 194 | req.body.access_token = 'mF_9.B5f-4.1JqM'; 195 | }) 196 | .fail(function(status) { 197 | expect(status).to.equal(400); 198 | done(); 199 | }) 200 | .authenticate(); 201 | }); 202 | 203 | it('should refuse request with token transmitted in both header field and URI query parameter', function(done) { 204 | chai.passport.use(strategy) 205 | .request(function(req) { 206 | req.headers['authorization'] = 'Bearer mF_9.B5f-4.1JqM'; 207 | req.query = {}; 208 | req.query.access_token = 'mF_9.B5f-4.1JqM'; 209 | }) 210 | .fail(function(status) { 211 | expect(status).to.equal(400); 212 | done(); 213 | }) 214 | .authenticate(); 215 | }); 216 | 217 | it('should refuse request with token transmitted in both form-encoded body parameter and URI query parameter', function(done) { 218 | chai.passport.use(strategy) 219 | .request(function(req) { 220 | req.body = {}; 221 | req.body.access_token = 'mF_9.B5f-4.1JqM'; 222 | req.query = {}; 223 | req.query.access_token = 'mF_9.B5f-4.1JqM'; 224 | }) 225 | .fail(function(status) { 226 | expect(status).to.equal(400); 227 | done(); 228 | }) 229 | .authenticate(); 230 | }); 231 | 232 | it('should throw if constructed without a verify function', function() { 233 | expect(function() { 234 | new Strategy(); 235 | }).to.throw(TypeError, 'HTTPBearerStrategy requires a verify function'); 236 | }); 237 | 238 | }); 239 | --------------------------------------------------------------------------------