diff --git a/lib/passport-oauth2.d.ts b/lib/passport-oauth2.d.ts new file mode 100644 index 0000000..da30853 --- /dev/null +++ b/lib/passport-oauth2.d.ts @@ -0,0 +1,72 @@ +// Type definitions for passport-oauth2 1.4 +// Project: https://github.com/jaredhanson/passport-oauth2#readme +// Definitions by: Pasi Eronen +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 2.3 + +import { Request } from 'express'; +import { Strategy } from 'passport'; + +declare class OAuth2Strategy implements Strategy { + name: string; + + constructor(options: OAuth2Strategy.StrategyOptions, verify: OAuth2Strategy.VerifyFunction); + constructor(options: OAuth2Strategy.StrategyOptionsWithRequest, verify: OAuth2Strategy.VerifyFunctionWithRequest); + + authenticate(req: Request, options?: any): void; + + userProfile(accessToken: string, done: (err?: Error | null, profile?: any) => void): void; + authorizationParams(options: any): object; + tokenParams(options: any): object; + parseErrorResponse(body: any, status: number): Error | null; +} + +declare namespace OAuth2Strategy { + type VerifyCallback = (err?: Error | null, user?: object, info?: object) => void; + + type VerifyFunction = + ((accessToken: string, refreshToken: string, profile: any, verified: VerifyCallback) => void) | + ((accessToken: string, refreshToken: string, results: any, profile: any, verified: VerifyCallback) => void); + type VerifyFunctionWithRequest = + ((req: Request, accessToken: string, refreshToken: string, profile: any, verified: VerifyCallback) => void) | + ((req: Request, accessToken: string, refreshToken: string, results: any, profile: any, verified: VerifyCallback) => void); + + interface _StrategyOptionsBase { + authorizationURL: string; + tokenURL: string; + clientID: string; + clientSecret: string; + callbackURL: string; + useExactURLs?: boolean; + } + interface StrategyOptions extends _StrategyOptionsBase { + passReqToCallback?: false; + } + interface StrategyOptionsWithRequest extends _StrategyOptionsBase { + passReqToCallback: true; + } + + type Strategy = OAuth2Strategy; + const Strategy: typeof OAuth2Strategy; + + class TokenError extends Error { + constructor(message: string | undefined, code: string, uri?: string, status?: number); + code: string; + uri?: string; + status: number; + } + + class AuthorizationError extends Error { + constructor(message: string | undefined, code: string, uri?: string, status?: number); + code: string; + uri?: string; + status: number; + } + + class InternalOAuthError extends Error { + constructor(message: string, err: any); + oauthError: any; + } +} + +export = OAuth2Strategy; diff --git a/lib/strategy.js b/lib/strategy.js index a0d50bd..c8a5fae 100644 --- a/lib/strategy.js +++ b/lib/strategy.js @@ -47,6 +47,9 @@ var passport = require('passport-strategy') * - `clientSecret` secret used to establish ownership of the client identifer * - `callbackURL` URL to which the service provider will redirect the user after obtaining authorization * - `passReqToCallback` when `true`, `req` is the first argument to the verify callback (default: `false`) + * - `useExactURLs` when `true`, `authorizationURL`, `tokenURL`, and `callbackURL` are passed exactly as they are typed. If `false`, + * HTTP protocol and hostname are automatically determined and appended. This can be used to get around strict + * callbackURL matching on the OAuth provider * * Examples: * @@ -92,6 +95,7 @@ function OAuth2Strategy(options, verify) { '', options.authorizationURL, options.tokenURL, options.customHeaders); this._callbackURL = options.callbackURL; + this._useExactURLs = options.useExactURLs || false; //Default to false for undefined this._scope = options.scope; this._scopeSeparator = options.scopeSeparator || ' '; this._key = options.sessionKey || ('oauth2:' + url.parse(options.authorizationURL).hostname); @@ -133,7 +137,7 @@ OAuth2Strategy.prototype.authenticate = function(req, options) { } var callbackURL = options.callbackURL || this._callbackURL; - if (callbackURL) { + if (callbackURL && !this._useExactURLs) { var parsed = url.parse(callbackURL); if (!parsed.protocol) { // The callback URL is relative, resolve a fully qualified URL from the diff --git a/package.json b/package.json index 560a959..b6320cd 100644 --- a/package.json +++ b/package.json @@ -17,12 +17,18 @@ "email": "jaredhanson@gmail.com", "url": "http://www.jaredhanson.net/" }, + "contributors": [ + { + "name": "Michael Demoret", + "email": "mdemoret@gmail.com" + } + ], "repository": { "type": "git", - "url": "git://github.com/jaredhanson/passport-oauth2.git" + "url": "git://github.com/mdemoret/passport-oauth2.git" }, "bugs": { - "url": "http://github.com/jaredhanson/passport-oauth2/issues" + "url": "http://github.com/mdemoret/passport-oauth2/issues" }, "license": "MIT", "licenses": [ @@ -49,5 +55,6 @@ }, "scripts": { "test": "node_modules/.bin/mocha --reporter spec --require test/bootstrap/node test/*.test.js test/**/*.test.js" - } + }, + "typings": "lib/passport-oauth2.d.ts" } diff --git a/test/oauth2.test.js b/test/oauth2.test.js index e829751..a378f30 100644 --- a/test/oauth2.test.js +++ b/test/oauth2.test.js @@ -360,6 +360,39 @@ describe('OAuth2Strategy', function() { expect(url).to.equal('https://www.example.com/oauth2/authorize?response_type=code&redirect_uri=https%3A%2F%2Fwww.example.net%2Fauth%2Fexample%2Fcallback%2Falt2&client_id=ABC123'); }); }); // that redirects to service provider with relative redirect URI option + + describe('that redirects to service provider with relative redirect URI option and useExactURLs on', function() { + var strategy = new OAuth2Strategy({ + authorizationURL: 'https://www.example.com/oauth2/authorize', + tokenURL: 'https://www.example.com/oauth2/token', + clientID: 'ABC123', + clientSecret: 'secret', + callbackURL: 'https://www.example.net/auth/example/callback', + useExactURLs: true, + }, + function(accessToken, refreshToken, profile, done) {}); + + + var url; + + before(function(done) { + chai.passport.use(strategy) + .redirect(function(u) { + url = u; + done(); + }) + .req(function(req) { + req.url = '/auth/example/callback/alt2'; + req.headers.host = 'www.example.net'; + req.connection = { encrypted: true }; + }) + .authenticate({ callbackURL: '/auth/example/callback/alt2' }); + }); + + it('should be redirected', function() { + expect(url).to.equal('https://www.example.com/oauth2/authorize?response_type=code&redirect_uri=%2Fauth%2Fexample%2Fcallback%2Falt2&client_id=ABC123'); + }); + }); // that redirects to service provider with relative redirect URI option and useExactURLs on describe('that redirects to authorization server using authorization endpoint that has query parameters with scope option', function() { var strategy = new OAuth2Strategy({