diff --git a/app/models/oauth2-access-token.js b/app/models/oauth2-access-token.js index a871ff1..b0dad96 100644 --- a/app/models/oauth2-access-token.js +++ b/app/models/oauth2-access-token.js @@ -10,8 +10,8 @@ const Schema = mongoose.Schema; const OAuth2AccessTokenSchema = new Schema({ token: { type: String, required: true, unique: true, index: 1 }, - user: { type: Schema.ObjectId, required: true, index: 1 }, - clientId: { type: Schema.ObjectId, required: true, index: 1 }, + user: { type: Schema.ObjectId, required: true, index: 1, ref: 'User' }, + client: { type: Schema.ObjectId, required: true, index: 1, ref: 'OAuth2Client' }, scope: { type: [String], required: true }, }); diff --git a/app/services/oauth2.js b/app/services/oauth2.js index 861696e..ba06419 100644 --- a/app/services/oauth2.js +++ b/app/services/oauth2.js @@ -14,16 +14,29 @@ const uuidv4 = require('uuid').v4; const striptags = require('striptags'); const oauth2orize = require('oauth2orize'); -const passport = require('passport'); - const generatePassword = require('password-generator'); +const passport = require('passport'); +const BasicStrategy = require('passport-http').BasicStrategy; +const ClientPasswordStrategy = require('passport-oauth2-client-password').Strategy; +const BearerStrategy = require('passport-http-bearer').Strategy; + const { SiteService/*, SiteError*/ } = require('../../lib/site-lib'); class OAuth2Service extends SiteService { constructor (dtp) { super(dtp, module.exports); + + this.populateOAuth2AccessToken = [ + { + path: 'user', + select: 'username username_lc displayName picture', + }, + { + path: 'client' + }, + ]; } async start ( ) { @@ -41,6 +54,23 @@ class OAuth2Service extends SiteService { this.server.deserializeClient(this.deserializeClient.bind(this)); } + registerPassport ( ) { + const verifyClient = this.verifyClient.bind(this); + const verifyHttpBearer = this.verifyHttpBearer.bind(this); + + const basicStrategy = new BasicStrategy(verifyClient); + this.log.info('registering Basic strategy', { basicStrategy }); + passport.use(basicStrategy); + + const clientPasswordStrategy = new ClientPasswordStrategy(verifyClient); + this.log.info('registering ClientPassword strategy', { clientPasswordStrategy }); + passport.use(clientPasswordStrategy); + + const httpBearerStrategy = new BearerStrategy(verifyHttpBearer); + this.log.info('registering Bearer strategy', { httpBearerStrategy }); + passport.use(httpBearerStrategy); + } + async serializeClient (client, done) { this.log.debug('serializeClient', { client }); return done(null, client.id); @@ -148,7 +178,7 @@ class OAuth2Service extends SiteService { var at = new OAuth2AccessToken({ token, user: ac.userId, - clientId: ac.clientId, + client: ac.clientId, scope: ac.scope, }); await at.save(); @@ -243,6 +273,35 @@ class OAuth2Service extends SiteService { .lean(); return client; } + + async verifyClient(clientId, clientSecret, done) { + const client = await this.getClientById(clientId); + if (!client) { + this.log.alert('OAuth2 request from unknown client', { clientId }); + return done(null, false); + } + if (client.clientSecret !== clientSecret) { + this.log.alert('OAuth2 client secret mismatch', { clientId }); + return done(null, false); + } + return done(null, client); + } + + async getAccessToken (accessToken) { + const token = await OAuth2AccessToken + .findOne({ token: accessToken }) + .populate(this.populateOAuth2AccessToken) + .lean(); + return token; + } + + async verifyHttpBearer (accessToken, done) { + const token = await this.getAccessToken(accessToken); + if (!token) { + return done(null, false); + } + return done(null, token.user, { scope: token.scope }); + } } module.exports = { diff --git a/lib/site-platform.js b/lib/site-platform.js index 4e1e2c4..dc037b3 100644 --- a/lib/site-platform.js +++ b/lib/site-platform.js @@ -312,6 +312,7 @@ module.exports.startWebServer = async (dtp) => { module.app.use(passport.initialize()); module.app.use(passport.session()); + module.services.oauth2.registerPassport(); module.app.use(module.services.session.middleware()); /* diff --git a/package.json b/package.json index 961b234..ed5fb6b 100644 --- a/package.json +++ b/package.json @@ -57,8 +57,11 @@ "oauth2orize": "^1.11.1", "otplib": "^12.0.1", "passport": "^0.5.2", + "passport-http": "^0.3.0", + "passport-http-bearer": "^1.0.1", "passport-local": "^1.0.0", "passport-oauth2": "^1.6.1", + "passport-oauth2-client-password": "^0.1.2", "password-generator": "^2.3.2", "pug": "^3.0.2", "qrcode": "^1.5.0", diff --git a/yarn.lock b/yarn.lock index f9f989f..e0c58c2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6347,6 +6347,20 @@ pascalcase@^0.1.1: resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= +passport-http-bearer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/passport-http-bearer/-/passport-http-bearer-1.0.1.tgz#147469ea3669e2a84c6167ef99dbb77e1f0098a8" + integrity sha512-SELQM+dOTuMigr9yu8Wo4Fm3ciFfkMq5h/ZQ8ffi4ELgZrX1xh9PlglqZdcUZ1upzJD/whVyt+YWF62s3U6Ipw== + dependencies: + passport-strategy "1.x.x" + +passport-http@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/passport-http/-/passport-http-0.3.0.tgz#8ee53d4380be9c60df2151925029826f77115603" + integrity sha512-OwK9DkqGVlJfO8oD0Bz1VDIo+ijD3c1ZbGGozIZw+joIP0U60pXY7goB+8wiDWtNqHpkTaQiJ9Ux1jE3Ykmpuw== + dependencies: + passport-strategy "1.x.x" + passport-local@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/passport-local/-/passport-local-1.0.0.tgz#1fe63268c92e75606626437e3b906662c15ba6ee" @@ -6354,6 +6368,13 @@ passport-local@^1.0.0: dependencies: passport-strategy "1.x.x" +passport-oauth2-client-password@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/passport-oauth2-client-password/-/passport-oauth2-client-password-0.1.2.tgz#4f378b678b92d16dbbd233a6c706520093e561ba" + integrity sha512-GHQH4UtaEZvCLulAxGKHYoSsPRoPRmGsdmaZtMh5nmz80yMLQbdMA9Bg2sp4/UW3PIxJH/143hVjPTiXaNngTQ== + dependencies: + passport-strategy "1.x.x" + passport-oauth2@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/passport-oauth2/-/passport-oauth2-1.6.1.tgz#c5aee8f849ce8bd436c7f81d904a3cd1666f181b"