@zhaochy/egg-oauth2-server
Version:
koa-oauth-server(node-oauth2-server) plugin for egg
164 lines (145 loc) • 4.32 kB
JavaScript
;
const AuthServer = require('oauth2-server');
const Request = require('oauth2-server').Request;
const Response = require('oauth2-server').Response;
const InvalidArgumentError = require('oauth2-server/lib/errors/invalid-argument-error');
const UnauthorizedRequestError = require('oauth2-server/lib/errors/unauthorized-request-error');
const ServerError = require('oauth2-server/lib/errors/server-error');
const SERVER = Symbol('server#oauth2');
/**
* replace response.
*/
function replaceResponse(res) {
// copy for response.headers.hasOwnProperty is undefined case
// https://github.com/oauthjs/node-oauth2-server/pull/486
const newResponse = {
headers: {},
};
for (const property in res) {
if (property !== 'headers') {
newResponse[property] = res[property];
}
}
for (const field in res.headers) {
newResponse.headers[field] = res.headers[field];
}
newResponse.header = newResponse.headers;
return newResponse;
}
/**
* Handle response.
*/
function handleResponse(ctx, response) {
ctx.body = response.body;
ctx.status = response.status;
ctx.set(response.headers);
}
/**
* Handle error.
*/
function handleError(ctx, e, response) {
if (response) {
ctx.set(response.headers);
}
if (e instanceof UnauthorizedRequestError) {
ctx.status = e.code;
} else {
ctx.body = {
message: (e instanceof ServerError && ctx.app.config.env === 'prod') ? 'Server Error' : e.message,
};
ctx.status = e.code;
}
return ctx.app.emit('error', e, ctx);
}
class OAuth2 {
constructor(config, model) {
if (!model) {
throw new InvalidArgumentError('Missing parameter: `model`');
}
this.config = config;
this.model = model;
}
get server() {
const { config, model: Model, ctx } = this;
const model = new Model(ctx);
this[SERVER] = new AuthServer(Object.assign(config, { model }));
return this[SERVER];
}
async execute(handle, ctx, options) {
let result = null;
this.ctx = ctx;
const request = new Request(ctx.request);
const response = new Response(replaceResponse(ctx.response));
try {
result = await this.server[handle](request, response, options);
// handleResponse(ctx, response);
} catch (e) {
if (this.config.errorHandler) {
this.config.errorHandler(ctx, e, response);
} else {
handleError(ctx, e, response);
}
}
return result;
}
token(options) {
return async (ctx, next) => {
if (ctx.path === this.config.loginURL && ctx.request.is('json')) {
ctx.logger.debug('Rewrite login request header, change content-type from application/json to application/x-www-form-urlencoded');
ctx.request.headers['content-type'] = 'application/x-www-form-urlencoded';
}
const token = await this.execute('token', ctx, options);
ctx.state.oauth = {
token,
};
if (token) {
await next();
}
};
}
authenticate(options) {
return async (ctx, next) => {
ctx.logger.debug('authenticate URL %s, whiteList: %j', ctx.path, this.config.whiteList);
if (this.config.whiteList.includes(ctx.path)) {
ctx.logger.debug('[OAuth] - request in whiteList, skip authentication');
await next();
return;
}
const token = await this.execute('authenticate', ctx, options);
if (token) {
const jwt = ctx.app.jwt.decode(token.accessToken);
ctx.state.oauth = token
ctx.state.jwt = jwt;
ctx.state.user = jwt.user;
ctx.logger.debug('[OAuth] - authentication passed');
await next();
}
};
}
authorize(options) {
options = options || {};
const self = this;
return async (ctx, next) => {
const opts = Object.assign({}, {
authenticateHandler: {
async handle(req) {
const { username, password } = req.body;
const user = await self.server.options.model.getUser(
username,
password
);
return user;
},
},
}, options);
const code = await this.execute('authorize', ctx, opts);
ctx.state.oauth = {
code,
};
if (code) {
await next();
}
};
}
}
module.exports = OAuth2;