GithubHelp home page GithubHelp logo

azard / egg-oauth2-server Goto Github PK

View Code? Open in Web Editor NEW
180.0 7.0 45.0 44 KB

:star2: OAuth2 server plugin for egg.js based on node-oauth2-server

License: MIT License

JavaScript 100.00%
egg-plugin oauth2 oauth2-server node-oauth2-server egg eggjs npm-package node nodejs

egg-oauth2-server's Introduction

egg-oauth2-server

NPM version build status Test coverage David deps Known Vulnerabilities npm download

Chinese Example | 中文样例教程(注意:文章里使用的是该插件 v1.x 版本,部分 API 名称有变化,主要流程一致)

egg-oauth2-server is a module that easily adds oauth2 capability to egg-based servers.

  • egg 2.x use egg-oauth2-server latest (Node >= 8.0.0)
  • egg 1.x use egg-oauth2-server 2.0.x (Node >= 6.0.0)

Install

$ npm i egg-oauth2-server --save

Usage

// {app_root}/config/plugin.js
exports.oAuth2Server = {
  enable: true,
  package: 'egg-oauth2-server',
};

// {app_root}/app/router.js
app.all('/user/token', app.oAuth2Server.token());
app.get('/user/authorize', app.oAuth2Server.authorize(), 'user.code');
app.get('/user/authenticate', app.oAuth2Server.authenticate(), 'user.authenticate');

// `ctx.state.oauth` has token or code data after middleware for controller.
// {app_root}/config/config.default.js
module.exports = config => {
  const exports = {};
  exports.oAuth2Server = {
    debug: config.env === 'local',
    grants: [ 'password' ],
  };
  return exports;
};

See test/fixtures/apps/oauth2-server-test/config/config.unittest.js for reference.

// {app_root}/app/extend/oauth.js
// or {app_root}/app/extend/oauth.ts
'use strict';

// need implement some follow functions
module.exports = app => {  
  class Model {
    constructor(ctx) {}
    async getClient(clientId, clientSecret) {}
    async getUser(username, password) {}
    async saveAuthorizationCode(code, client, user) {}
    async getAuthorizationCode(authorizationCode) {}
    async revokeAuthorizationCode(code) {}
    async saveToken(token, client, user) {}
    async getAccessToken(bearerToken) {}
    async revokeToken(token) {}
  }  
  return Model;
};

For full description, check out https://www.npmjs.com/package/oauth2-server.

Examples

A simple password-mode OAuth 2.0 server. Full code at test/fixtures/apps/oauth2-server-test/app/extend/oauth.js

password mode app.oauth.token() lifecycle

getClient --> getUser --> saveToken

password mode app.oauth.authenticate() lifecycle

Only getAccessToken

authorization_code mode app.oauth.authorize() lifecycle

getClient --> getUser --> saveAuthorizationCode

authorization_code mode app.oauth.token() lifecycle

getClient --> getAuthorizationCode --> revokeAuthorizationCode --> saveToken

authorization_code mode app.oauth.authenticate() lifecycle

Only getAccessToken

Questions & Suggestions

Please open an issue. PRs are welcomed too.

License

MIT

egg-oauth2-server's People

Contributors

azard avatar dead-horse avatar ithinco avatar mingyuan-xia avatar ole3021 avatar senique avatar shyser avatar sm2017 avatar thonatos avatar zemzheng 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  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  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

egg-oauth2-server's Issues

几点建议

非常赞,感谢回馈。

以下是几点建议:

  • 配置修改exports['oauth2-server'] 改为 exports.oauth2Server
  exports.oauth2Server = {
    debug: process.env.NODE_ENV !== 'production',
    grants: [ 'password' ],
    model: oauth_model(app),
  };
  • 插件也修改为
exports.oauth2Server = {
  enable: true,
  package: 'egg-oauth2-server',
};
  • model 不用用户手动初始化,通过 Loader API 自动加载,可以参考 egg-sequelizeegg-view-nunjucks 的实现

  • app.js 里面的 console.log 可以改为 app.coreLogger,参见 日志

  • debug: process.env.NODE_ENV !== 'production' 可以考虑直接读取 app.config.env === 'local' 做转换

  • agent.js 没用可以去掉

  • index.js 也是多余的,你是用了 simple 那个 boilerplate ?应该用 plugin 那个,建议用 egg-boilerplate-plugin 重新初始化下,现在看到有不少不需要的代码,如 package.json 的 scripts 等。

lib下的server.js有错

原来代码如下:
   const user = yield server.options.model.getUser(
                username,
                password,
      );
password后面多了个逗号

获取token老是报错

获取token body里面传哪些参数呢,希望文档可以写下
{
"error": "invalid_client",
"error_description": "Invalid client: cannot retrieve client credentials"
}

options.headers.hasOwnProperty is not a function

2017-09-21 16:37:56,696 ERROR 32793 [-/127.0.0.1/-/10737ms GET /user/token] nodejs.TypeError: options.headers.hasOwnProperty is not a function
at new Response (/Users/tusm/github/mygithub/egg-cms/node_modules/oauth2-server/lib/response.js:16:25)
at Object. (/Users/tusm/github/mygithub/egg-cms/node_modules/egg-oauth2-server/lib/server.js:63:24)
at Generator.next ()
at Object.dispatch (/Users/tusm/github/mygithub/egg-cms/node_modules/koa-router/lib/router.js:334:12)
at dispatch.next ()
at onFulfilled (/Users/tusm/github/mygithub/egg-cms/node_modules/co/index.js:65:19)
at /Users/tusm/github/mygithub/egg-cms/node_modules/co/index.js:54:5
at Promise ()
at Object.co (/Users/tusm/github/mygithub/egg-cms/node_modules/co/index.js:50:10)

// Store the headers in lower case.
  for (var field in options.headers) {
    if (options.headers.hasOwnProperty(field)) {
      this.headers[field.toLowerCase()] = options.headers[field];
    }
  }

‌‌typeof this.ctx.response.headers
‌object
‌‌typeof this.ctx.response.proto
‌object
‌‌typeof this.ctx.response.headers.proto
‌undefined
tips: egg issues

accessTokenLifetime 不支持无限期

config

config.oAuth2Server = {
    debug: appInfo.env === 'local',
    grants: ['password'],
    accessTokenLifetime: null,
    refreshTokenLifetime: null
}

Error

 nodejs.invalid_argument: Missing parameter: `accessTokenLifetime`
    at new InvalidArgumentError (/Volumes/Mac/Projects/Node/liubangback/node_modules/oauth2-server/lib/errors/invalid-argument-error.js:21:14)
    at new TokenHandler (/Volumes/Mac/Projects/Node/liubangback/node_modules/oauth2-server/lib/handlers/token-handler.js:43:11)
    at OAuth2Server.token (/Volumes/Mac/Projects/Node/liubangback/node_modules/oauth2-server/lib/server.js:74:10)
    at Object.<anonymous> (/Volumes/Mac/Projects/Node/liubangback/node_modules/egg-oauth2-server/lib/server.js:82:36)
    at Generator.next (<anonymous>)
    at Object.dispatch (/Volumes/Mac/Projects/Node/liubangback/node_modules/koa-router/lib/router.js:334:5)
    at dispatch.next (<anonymous>)
    at onFulfilled (/Volumes/Mac/Projects/Node/liubangback/node_modules/co/index.js:65:19)
    at /Volumes/Mac/Projects/Node/liubangback/node_modules/co/index.js:54:5
    at new Promise (<anonymous>)
    at Object.co (/Volumes/Mac/Projects/Node/liubangback/node_modules/co/index.js:50:10)
    at Object.toPromise (/Volumes/Mac/Projects/Node/liubangback/node_modules/co/index.js:118:63)
    at next (/Volumes/Mac/Projects/Node/liubangback/node_modules/co/index.js:99:29)
    at onFulfilled (/Volumes/Mac/Projects/Node/liubangback/node_modules/co/index.js:69:7)
    at /Volumes/Mac/Projects/Node/liubangback/node_modules/co/index.js:54:5
    at new Promise (<anonymous>)
    at Object.co (/Volumes/Mac/Projects/Node/liubangback/node_modules/co/index.js:50:10)
    at Object.toPromise (/Volumes/Mac/Projects/Node/liubangback/node_modules/co/index.js:118:63)
    at next (/Volumes/Mac/Projects/Node/liubangback/node_modules/co/index.js:99:29)
    at onFulfilled (/Volumes/Mac/Projects/Node/liubangback/node_modules/co/index.js:69:7)
    at /Volumes/Mac/Projects/Node/liubangback/node_modules/co/index.js:54:5
    at new Promise (<anonymous>)
    at Object.co (/Volumes/Mac/Projects/Node/liubangback/node_modules/co/index.js:50:10)
    at Object.toPromise (/Volumes/Mac/Projects/Node/liubangback/node_modules/co/index.js:118:63)
    at next (/Volumes/Mac/Projects/Node/liubangback/node_modules/co/index.js:99:29)
    at onFulfilled (/Volumes/Mac/Projects/Node/liubangback/node_modules/co/index.js:69:7)
    at /Volumes/Mac/Projects/Node/liubangback/node_modules/co/index.js:54:5
    at new Promise (<anonymous>)

Missing parameter: `grant_type`

I have try egg-auth2-server as shown in test.
But I got the error message : "Missing parameter: grant_type" with my request as follows:

request(${host}/user/token, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', Authorization: 'Basic bXlfYXBwOm15X3NlY3JldA==', }, data: { grant_type: 'password', username: 'egg-oauth2-server', password: 'azard', }, });

'oauth.js' and 'config.default.js' are the same as shown in test dir.
Wish to get some help, thanks.

Issue with eggjs 2.0

在 egg.js 中使用在获取token的saveToken之后会报如下错误

egg: 2.0.0 
node: 8.9.0
os: mac 10.13.1
2017-11-29 03:22:00,577 ERROR 58549 [-/::1/-/126ms POST /login] nodejs.TypeError: undefined is not a function
    at Object.<anonymous> (/Users/ole3021/ducklab/ci-backend/node_modules/egg-oauth2-server/lib/server.js:102:20)
    at Generator.next (<anonymous>)
    at onFulfilled (/Users/ole3021/ducklab/ci-backend/node_modules/co/index.js:65:19)
    at <anonymous>

经初步排查错误代码发生在lib/server.js中第101行的if (token) { yield* next; }, 其中next不是可yield的generator函数,而是ctx对象。

修正方法可使用if(token) ctx.body = token 来替代(初步解决方法)

问题应该是对egg新版本的适配问题, 刚刚发现, 有时间的话会提pr,另外希望可以尽快适配并做好测试。:)

How to use authenticate() as global middleware?

when use egg-oauth2-server plugin as global middleware:

// app/middleware/auth.js

module.exports = options => {
  return async (ctx, next) => {
    try {
      const auth = await ctx.app.oAuth2Server.authenticate();
      console.log(auth); 
      await next();
    } catch (err) {
      ctx.logger.error(err);
    }
  };
};
// config/config.default.js

config.middleware = [ 'auth' ];

then output: [AsyncFunction]

when use authenticate() in egg controller:

// app/controller/debug.js

async index() {
  const { app, ctx } = this;
  const res = await app.oAuth2Server.authenticate();
  ctx.body = res;
}

then throw:

{
  "code": "ERR_INVALID_ARG_TYPE",
  "message": "The \"string\" argument must be one of type string, Buffer, or ArrayBuffer. Received type undefined",
  "stack": "TypeError [ERR_INVALID_ARG_TYPE]: The \"string\" argument must be one of type string, Buffer, or ArrayBuffer. Received type undefined\n    at Function.byteLength (buffer.js:494:11)\n    at respond (D:\\Develop\\projects\\zhifu\\api2\\node_modules\\koa\\lib\\application.js:245:25)\n    at handleResponse (D:\\Develop\\projects\\zhifu\\api2\\node_modules\\koa\\lib\\application.js:149:34)\n    at processTicksAndRejections (internal/process/task_queues.js:85:5)",
  "name": "TypeError",
  "status": 500
}

关于revokeToken/Logout的实现

在文档说明中有revoceToken的实现,但是再实例的抽象中没有logout之类的方法,是不是缺少了关于logout的实现?还是有别的什么用法?

请教一个问题

请问调用authorize()这个函数,目前是把网页直接返回,有没有办法返回URL

关于 model 的讨论

目前的设计方式

  • 用户在 app/extend/oauth.js 中提供策略
  • 插件会在 app.js 中调用 loadFile

遇到问题:

  • oauth.js 一般都会调用 service 进行存储,目前的方式无法支持
  • oauth.js 的 API 都是 callback 方式的,不优雅

建议提案

主插件 egg-oauth2-server

职责:

  • 类似 egg-view 和 egg-passport,提供统一的约定和挂载方式
  • 提供 app.oauth2Server.use(StrategyClass)
    • use 里面可以调用 loadToContext 等把 class 注册到 ctx
  • 提供 app.oauth2Server.Strategy 基类
    • interface 设计都改为 promise 或 generator 类

用户使用方式

// {app_root}/app.js
module.exports = app => {
  const Strategy = require('./lib/oauth2_strategy');
  app.oauth2Server.use(Strategy);
}

// {app_root}/lib/oauth2_strategy.js
module.exports = app => {
  return class MyStrategy extends app.oauth2Server.Strategy {
    constructor(ctx) {
      super(ctx);
    }

    * getAccessToken() {
      return yield ctx.service.xxx();
    }
  }
}

进一步扩展

  • 上面的 oauth2_strategy.js 可以独立为二级插件的方式
  • 即根据不同的业务场景,封装 egg-oauth2-server-firebase / egg-oauth2-server-alibaba
  • 应用开发者直接安装主插件和二级插件,然后在 app.jsuse 下即可
// {app_root}/app.js
module.exports = app => {
  const Strategy = require('./lib/oauth2_strategy');
  app.oauth2Server.use(Strategy);
}

// {app_root}/lib/oauth2_strategy.js
module.exports = app => {
  return class MyStrategy extends app.oauth2Server.Strategy {
    constructor(ctx) {
      super(ctx);
    }

    * getAccessToken() {
      return yield ctx.service.xxx();
    }
  }
}

token存储位置

我想问下,这个token的存储是存储在数据库里面吗 如果多台服务器要怎么取出这个token相关信息

有关client grants属性的问题

您好:
想请教一下
AuthorizeHandler.prototype.getClient = function(request) {
var clientId = request.body.client_id || request.query.client_id;

if (!clientId) {
throw new InvalidRequestError('Missing parameter: client_id');
}

if (!is.vschar(clientId)) {
throw new InvalidRequestError('Invalid parameter: client_id');
}

var redirectUri = request.body.redirect_uri || request.query.redirect_uri;

if (redirectUri && !is.uri(redirectUri)) {
throw new InvalidRequestError('Invalid request: redirect_uri is not a valid URI');
}
return promisify(this.model.getClient, 2).call(this.model, clientId, null)
.then(function(client) {
if (!client) {
throw new InvalidClientError('Invalid client: client credentials are invalid');
}

  if (!client.grants) {
    throw new InvalidClientError('Invalid client: missing client `grants`');
  }

  if (!_.includes(client.grants, 'authorization_code')) {
    throw new UnauthorizedClientError('Unauthorized client: `grant_type` is invalid');
  }

  if (!client.redirectUris || 0 === client.redirectUris.length) {
    throw new InvalidClientError('Invalid client: missing client `redirectUri`');
  }

  if (redirectUri && !_.includes(client.redirectUris, redirectUri)) {
    throw new InvalidClientError('Invalid client: `redirect_uri` does not match client value');
  }
  return client;
});

};
在鉴权第一步的时候,首先会调用getClient方法获取我们注册的app信息,这里会验证grants这个属性,我不太理解这个属性到底有什么含义,或者说是他的意义体现在哪里?
因为我现在也在做一个鉴权的服务,其中应用信息是另一个项目进行创建注册的,在创建应用的时候,grants这个属性现在是必须要的,而且值也是固定的几个值,就是不太理解为什么要验证这个属性

Saving context in application is an anti-pattern

I found on some occasions, the Model being instantiated with the last request context, then I found this:

this.ctx = ctx;

You should never save ctx in application ever. This is my fix:

{
  getServer(ctx) {
    if (!ctx[SERVER]) {
      const { config, model: Model } = this;
      const model = new Model(ctx);
      ctx[SERVER] = new AuthServer(Object.assign(config, { model }));
      return ctx[SERVER];
    }
    return ctx[SERVER];
  }
}

Is implicit grant mode supported?

node-oauth2-server support implicit grant mode now, so is this plugin supported? BTW, if I want to use more than one mode, how do I define the router?

the ctx of Model can't be changed

this.ctx can't be changed on each request after constructed.

module.exports = (app) => {
  class Model {
    constructor(ctx) {
      this.ctx = ctx;
    }
    test() {
      console.log(this.ctx.request.query);
    }
  }
  return Model;
};

status code must be a number

018-07-29 15:50:49,771 ERROR 84272 [-/::1/-/8ms GET /user/token] nodejs.AssertionError [ERR_ASSERTION]: status code must be a number

support app/extend/oauth.ts

both

  • app/extend/oauth.ts
  • app/extend/oauth.js

should be acceptable

是否可以考虑允许使用 app/extend/oauth.ts。
当我的项目是用 ts 来编写的时候 dev/debug 比较方便点。

accessTokenLifetime 配置无效

config/config.default.js

oAuth2Server: {
  debug: true,
  grants: [ 'password', 'authorization_code', 'refresh_token' ],
  accessTokenLifetime: 7200,
  refreshTokenLifetime: 1209600
}

get token response

{
"accessToken": "1a4e7b8531f274fce992d33cabe62b4545327e7f",
"accessTokenExpiresAt": "2018-10-15T04:03:13.164Z",
"refreshToken": "b9c95fba7f1e9112e12d0f25ead5162e88c305a3",
"refreshTokenExpiresAt": "2018-09-29T04:03:13.164Z",
...
}

accessTokenExpiresAt 比createdAt时间大了一个月

OAuth2.0授权服务验证失败

2017-09-21 6 03 25

fetch('http://127.0.0.1:7001/user/token',{method: 'POST', mode:'cors',credentials: 'include',headers:{"Content-Type":"application/x-www-form-urlencoded",Authorization: 'Basic bXlfYXBwOm15X3NlY3JldA=='},body:'username=egg-oauth2-server&password=azard&grant_type=password'}) .then(function(res){return res.json()}).then(function(res){console.log(res)})

下面的Authorization是上一个接口的token_type + access_token ;

fetch('http://127.0.0.1:7001/user/authenticate',{method: 'GET', mode:'cors',credentials: 'include',headers:{Authorization: 'Bearer 51375d61bc4f4f7e9661d9a71f0745441024ee7c'}}) .then(function(res){return res.json()}).then(function(res){console.log(res)})

返回错误

{"error":"server_error","error_description":"Server error: `accessTokenExpiresAt` must be a Date instance"}

好像是格式有问题

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.