From 8fbc0d210ec31a4dc93beaf11689ac6cea6f72c9 Mon Sep 17 00:00:00 2001 From: Max Truxa Date: Thu, 9 Mar 2017 23:55:11 +0100 Subject: [PATCH 01/11] Add documentation --- .gitignore | 6 +- README.md | 105 +- docs/Makefile | 153 +++ docs/_static/custom.css | 63 ++ docs/_static/favicon.ico | Bin 0 -> 13094 bytes docs/api/errors/access-denied-error.rst | 82 ++ docs/api/errors/index.rst | 34 + docs/api/errors/invalid-argument-error.rst | 84 ++ docs/api/errors/invalid-client-error.rst | 82 ++ docs/api/errors/invalid-grant-error.rst | 82 ++ docs/api/errors/invalid-request-error.rst | 82 ++ docs/api/errors/invalid-scope-error.rst | 82 ++ docs/api/errors/invalid-token-error.rst | 82 ++ docs/api/errors/oauth-error.rst | 124 +++ docs/api/errors/server-error.rst | 84 ++ docs/api/errors/unauthorized-client-error.rst | 82 ++ .../api/errors/unauthorized-request-error.rst | 89 ++ .../errors/unsupported-grant-type-error.rst | 82 ++ docs/api/oauth2-server.rst | 294 ++++++ docs/api/request.rst | 134 +++ docs/api/response.rst | 148 +++ docs/conf.py | 276 +++++ docs/docs/getting-started.rst | 106 ++ docs/index.rst | 87 ++ docs/make.bat | 190 ++++ docs/misc/extension-grants.rst | 8 + docs/misc/migrating-v2-to-v3.rst | 8 + docs/model/overview.rst | 129 +++ docs/model/spec.rst | 981 ++++++++++++++++++ docs/npm_conf.py | 49 + 30 files changed, 3741 insertions(+), 67 deletions(-) create mode 100644 docs/Makefile create mode 100644 docs/_static/custom.css create mode 100644 docs/_static/favicon.ico create mode 100644 docs/api/errors/access-denied-error.rst create mode 100644 docs/api/errors/index.rst create mode 100644 docs/api/errors/invalid-argument-error.rst create mode 100644 docs/api/errors/invalid-client-error.rst create mode 100644 docs/api/errors/invalid-grant-error.rst create mode 100644 docs/api/errors/invalid-request-error.rst create mode 100644 docs/api/errors/invalid-scope-error.rst create mode 100644 docs/api/errors/invalid-token-error.rst create mode 100644 docs/api/errors/oauth-error.rst create mode 100644 docs/api/errors/server-error.rst create mode 100644 docs/api/errors/unauthorized-client-error.rst create mode 100644 docs/api/errors/unauthorized-request-error.rst create mode 100644 docs/api/errors/unsupported-grant-type-error.rst create mode 100644 docs/api/oauth2-server.rst create mode 100644 docs/api/request.rst create mode 100644 docs/api/response.rst create mode 100644 docs/conf.py create mode 100644 docs/docs/getting-started.rst create mode 100644 docs/index.rst create mode 100644 docs/make.bat create mode 100644 docs/misc/extension-grants.rst create mode 100644 docs/misc/migrating-v2-to-v3.rst create mode 100644 docs/model/overview.rst create mode 100644 docs/model/spec.rst create mode 100644 docs/npm_conf.py diff --git a/.gitignore b/.gitignore index 3c3629e64..820d105cc 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ -node_modules +node_modules/ +docs/_build/ +__pycache__/ +*.pyc + diff --git a/README.md b/README.md index f7d305f81..4a22598ec 100644 --- a/README.md +++ b/README.md @@ -1,99 +1,72 @@ -Complete, compliant and well tested module for implementing an OAuth2 server in [node.js](https://nodejs.org/). +# oauth2-server - [![NPM Version][npm-image]][npm-url] - [![Build Status][travis-image]][travis-url] - [![NPM Downloads][downloads-image]][downloads-url] +[![npm Version][npm-image]][npm-url] +[![npm Downloads][downloads-image]][downloads-url] +[![Test Status][travis-image]][travis-url] +[![MIT Licensed][license-image]][license-url] -# Quick Start +Complete, compliant and well tested module for implementing an OAuth2 server in [Node.js](https://nodejs.org). - The _node-oauth2-server_ module is framework-agnostic but there are several wrappers available for popular frameworks such as [express](https://github.com/oauthjs/express-oauth-server) and [koa 2](https://github.com/oauthjs/koa-oauth-server). - Using the _express_ wrapper (_recommended_): +## Installation -```js -var express = require('express'); -var oauthServer = require('express-oauth-server'); -var app = express(); +```bash +npm install oauth2-server +``` -var oauth = new oauthServer({ model: model }); +The *oauth2-server* module is framework-agnostic but there are several officially supported wrappers available for popular HTTP server frameworks such as [Express](https://npmjs.org/package/express-oauth-server) and [Koa](https://npmjs.org/package/koa-oauth-server). If you're using one of those frameworks it is strongly recommended to use the respective wrapper module instead of rolling your own. -app.use(oauth.authenticate()); -app.get('/', function (req, res) { - res.send('Hello World'); -}) +## Features -app.listen(3000); -``` +- Supports `authorization_code`, `client_credentials`, `refresh_token` and `password` grant, as well as *extension grants*, with scopes. +- Can be used with *promises*, *Node-style callbacks*, *ES6 generators* and *async*/*await* (using [Babel](https://babeljs.io)). +- Fully [RFC 6749](https://tools.ietf.org/html/rfc6749.html) and [RFC 6750](https://tools.ietf.org/html/rfc6749.html) compliant. +- Implicitly supports any form of storage, e.g. *PostgreSQL*, *MySQL*, *MongoDB*, *Redis*, etc. +- Complete [test suite](https://github.com/oauthjs/node-oauth2-server/tree/master/test). - Using this module directly (_for custom servers only_): -```js -var Request = require('oauth2-server').Request; -var oauthServer = require('oauth2-server'); +## Documentation -var oauth = new oauthServer({ model: model }); +[Documentation](https://oauth2-server.readthedocs.io) is hosted on Read the Docs. -var request = new Request({ - headers: { authorization: 'Bearer foobar' } -}); -oauth.authenticate(request) - .then(function(data) { - // Request is authorized. - }) - .catch(function(e) { - // Request is not authorized. - }); -``` +## Examples - _Note: see the documentation for the [specification][wiki-model-specification] of what's required from the model._ +Most users should refer to our [Express](https://github.com/oauthjs/express-oauth-server/tree/master/examples) or [Koa](https://github.com/oauthjs/koa-oauth-server/tree/master/examples) examples. -# Features +Examples for v3 are yet to be made. Examples for v2 can still be found [here](https://github.com/oauthjs/node-oauth2-server/tree/b36a06b445ad0a676e6175d68a8bd0b2f3353dbf/examples). - - Supports `authorization_code` (with scopes), `client_credentials`, `password`, `refresh_token` and custom `extension` grant types. - - Can be used with _node-style_ callbacks, promises and ES6 _async_/_await_. - - Fully [RFC6749](https://tools.ietf.org/html/rfc6749) and [RFC6750](https://tools.ietf.org/html/rfc6750) compliant. - - Implicitly supports any form of storage e.g. _PostgreSQL_, _MySQL_, _Mongo_, _Redis_, _etc_. - - Full test suite. +[//]: # (If you're implementing a custom server, we have many examples available:) -# Documentation +[//]: # (- A simple **password** grant [example](https://github.com/oauthjs/node-oauth2-server/tree/master/examples/password).) +[//]: # (- A more complex **password** and **refresh_token** grant [example](https://github.com/oauthjs/node-oauth2-server/tree/master/examples/refresh-token).) +[//]: # (- An advanced **password**, **refresh_token** and **authorization_code** grant [example](https://github.com/oauthjs/node-oauth2-server/tree/master/examples/authorization-code) with scopes.) - - [Server options][wiki-server-options] - - [Model specification][wiki-model-specification] - - [Authorization Code][wiki-model-specification] - - [Client Credentials][wiki-model-specification] - - [Password][wiki-model-specification] - - [Refresh token][wiki-model-specification] - - [Custom extension][wiki-model-specification] -# Examples +## Upgrading from 2.x - Most users should refer to our [express](https://github.com/seegno/express-oauth-server/tree/master/examples) or [koa](https://github.com/thomseddon/koa-oauth-server/tree/master/examples) examples. If you're implementing a custom server, we have many examples available: +This module has been rewritten using a promise-based approach, introducing changes to the API and model specification. - - A simple **password** grant authorization [example](examples/password). - - A more complex **password** and **refresh_token** [example](examples/refresh-token). - - An advanced **password**, **refresh_token** and **authorization_code** (with scopes) [example](examples/authorization-code). +Please refer to our [3.0 migration guide](https://github.com/oauthjs/node-oauth2-server/wiki/Migrating-from-2-x-to-3-x) for more information. -# Upgrading from 2.x - This module has been rewritten with a promise-based approach and introduced a few changes in the model specification. +## Tests - Please refer to our [3.0 migration guide][wiki-migrating-from-2x-to-3x] for more information. +To run the test suite, install dependencies, then run `npm test`: -## License +```bash +npm install +npm test +``` - [MIT](LICENSE) - [npm-image]: https://img.shields.io/npm/v/oauth2-server.svg [npm-url]: https://npmjs.org/package/oauth2-server -[travis-image]: https://img.shields.io/travis/oauthjs/node-oauth2-server/master.svg -[travis-url]: https://travis-ci.org/oauthjs/node-oauth2-server [downloads-image]: https://img.shields.io/npm/dm/oauth2-server.svg [downloads-url]: https://npmjs.org/package/oauth2-server +[travis-image]: https://img.shields.io/travis/oauthjs/node-oauth2-server/master.svg +[travis-url]: https://travis-ci.org/oauthjs/node-oauth2-server +[license-image]: https://img.shields.io/badge/license-MIT-blue.svg +[license-url]: https://raw.githubusercontent.com/oauthjs/node-oauth2-server/master/LICENSE - -[wiki-model-specification]: https://github.com/oauthjs/node-oauth2-server/wiki/Model-specification -[wiki-migrating-from-2x-to-3x]: https://github.com/oauthjs/node-oauth2-server/wiki/Migrating-from-2-x-to-3-x -[wiki-server-options]: https://github.com/oauthjs/node-oauth2-server/wiki/Server-options diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 000000000..f8cc19dbb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,153 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/oauth2-server.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/oauth2-server.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/oauth2-server" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/oauth2-server" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/docs/_static/custom.css b/docs/_static/custom.css new file mode 100644 index 000000000..4444839ad --- /dev/null +++ b/docs/_static/custom.css @@ -0,0 +1,63 @@ + +/* fix word-wrap for responsive tables, as described here: + * http://rackerlabs.github.io/docs-rackspace/tools/rtd-tables.html */ +@media screen and (min-width: 767px) { + .wy-table-responsive table td { + white-space: normal !important; + } + .wy-table-responsive { + overflow: visible !important; + } +} + +/* ensure that smaller tables span the whole page width */ +.rst-content table.docutils { + width: 100% !important; +} + +/* "Name" column of "arguments" tables */ +.rst-content table.docutils th:nth-child(1), +.rst-content table.docutils td:nth-child(1) { + width: 35% !important; + word-break: break-all !important; +} + +/* "Type" column of "arguments" tables */ +.rst-content table.docutils th:nth-child(2), +.rst-content table.docutils td:nth-child(2) { + width: 20% !important; + word-break: normal !important; +} + +/* "Description" column of "arguments" tables */ +/*.rst-content table.docutils th:nth-child(3), +.rst-content table.docutils td:nth-child(3) { +}*/ + +/* use a slightly smaller font size for table contents */ +.rst-content table.docutils th, +.rst-content table.docutils td { + font-size: 85% !important; +} + +/* reduce left-/right-padding of literals from 5px to 3px */ +.rst-content code.literal { + padding-left: 3px !important; + padding-right: 3px !important; +} + +/* external links generated by the :rfc: role are surrounded by + * tags which doesn't look good in floating text */ +.rst-content a.rfc strong { + font-weight: normal !important; +} + +/* default style for blockquotes is just indentation; + * disable indentation and instead use custom background color */ +.rst-content blockquote { + margin-left: 0 !important; + padding: 10px !important; + background-color: #fff8dc !important; + border-left: 2px solid #ffeb8e !important; +} + diff --git a/docs/_static/favicon.ico b/docs/_static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..522790e1e0f8e70d5bc23bb081f8bfb4a8ac3cd9 GIT binary patch literal 13094 zcmeG?2b3h$R<~Y-uF%!Fr>DC*P3NwvPP4r;3(ZW=?#ym4Q8Gx9qe z1_VU~^(*p4K@5O=L5yGo1BiY^42S_##PsprS5-Yd!w!7T@%TNTy|dL_UGLo--hHnY zKmZA1F~DyXJ|+Vk0DyAY`~7hh;1l?)-}iq<08YniZO!}LngjTB9-xIdNCFUz)4?yg zfrQ~*bk8q7u=PU_1gO{RuxHO67>!2IXf!~Qq^*;7__b@-E;#YT6Zv)W$tS}ZXPg1& zop&CbefHU$)(&W37zP}7+;MQ$S!coO>MERh=9#c>-#&gFJa`Z`Ha0koV~;%+A`w)0 z8=QLTsX$}IXJust9LIrtJ`cHE4zk%S=Y7HnC%{oh9mQpvMW^|voN@|ing;QBoZ}E~ zu~>vssl>nM=jTD!bx;(A%Q=}$rX+}Wv)Sao37>FUtrpyN+ih^yU3WpJ)8WsO$t0gc zx6bgL&%{SkKciwpi&OP^Bt_$JOyih0v#NV$a5&`p5I)(J=8(UieDcXD{`1d2 z4}-yg(}-bU68?b$2QUWveEw;toi>%9>_+~2<&{_9?z``XTW`G;UVZgdIRE_fxx6Gh z=}EpIISI(;wOWnSO{dd9_I&7}hqw<(=X>wH7cRg2atQ24b5B40bdF0OYZ&}w1M-b5 z%bX9{pL8L7iseeB!u?46k3RZn9#>NWctbweyLT_QC+U_-r67~Za9ZTs-Me?g1s7bv zb)&f64kR1J`b8IA#OII=f?PP}m}9uTC@%fL%m}8GS{)Er#4;FU~cP}mPIpWCCQ7fzC@ycS-=C3ITjl1h`J{uAS@~jMH4gAzZ4nWxUq#qI%5% zOXc=DOF2U|j^c{)wpy)%ZQI;Oq0|w*%FX`|rOW9)0vtc;bmC;F)Khf#;rk z4qkZS1$g=8mwArA{`%|i_Sl+JyD>9kQt4)tj40H|A|b&N?9nZZ~~J?m(j2!%D8~x1X)Br0IXBx z9LCucA7+VR2Pv@Bu;)!k=f_0A?O|C4NfZa8B?a+&_{FGc6+12tPr?=vV-m}cj!esn zyfhk!G9wPTFiN`#$4ec52Szg{V8f7IW(|9a}HdxD2(}}~Pv+9Or0ZM(HInPuTi-u9h~A;Oe1eP`EQN>@iN|sd z*>7A(&^XeRL?xF?2E&jn?4$8Xg&P?71kZ82x{jF^&f2JSdmuNR8sDw_eizml^;Ox)FB zR);ATla)fDGMfVvC(?5PFk29Mn)U0n3atdiy<@G>17NaIRStFTTo(o zt7FZDD&xr}@0m%HjeV=*@y0gFGZPMROP2a<0`)1lWrXwbn)WH$&V35?v(yt%|4jP= z>ILmz=_Ab=pQcpv@E$tgReNk5DjiTFi?NXsoY&#vdXwHxJ?TE2DA9eb6 zE`|%aspu-G(NfpRrbdTG*cip^eo2B{#}>eixY*q2nw;-Bdi_u=))#UE+b~s#cJX-5 z&38>O2M&N*>Ra>g)na|2xM*pHja$q@8^G$?vew__V(}It{ZeW>!uouvZ$h@;9fUzK zF+i~|ks<{b47U^2dgatMk>(3H5uj7f(HG#MpoX1-U_hyDxghmQ3tN0q&tlhy5YAyY zB1@W~NrtAV*y33o*GBw)t~q0+HL-^}dJxv}Tnh9kXc0j%1<2*8Ps;XUtD6XwGAhPF zHC4$}6jM=6Nj5bh9GrHv+m-43)i4(x5o9f_;$+2?l(43T4Z+ZlRB zHKGWf3I$)F19G4D%^(|wqDgt2-^2JX*(gp=GZe${=9^uaJ9%vM{i9kgk3K|%0E*-a zPdQL~23KGl?NO5mGIEYeLbw}DUS3|x$taH;^)&88xJ5^~4oydKBuUdGrLZ}_r|?Ib zH{Z=2NeYkh-h50C3If^KJw+&i8HTWl4GDph=b0AFS4~C02r%`jKucM4b(BKGn{RpZ zEzkV2t||D>&`JUQo}nv}j;{0MadLeAIO6GxG2#Vf4h_knEOB{wq$3B{qGXDFiEmm zPoR=%Q0kz4IONMUFMjITp679uk`RL;JRtH*Sw%ya=u!~SS-)_tGhZN}j52jF^24R2 z-DO=+qAJ?cXWmH%HKJDX(@_R3DpQ8!OdYp3QIsc4l?7qGUGl?uGFgcYJdLr6Xc!at zA>lW_qq0JvclCh$WKt{jd?A74!B&yyqo71Q;yB-KFL>J0=bG9p6A{&vLH2}}F;q@|n*ig$g6?+E^>V)_;Sx|PgX~ZtFxtIr<-OAbsb*QeJZ}l-03_W4-)uZlg`y?ze+&+Hrj{M9+l3wF`D@E*^l5{o!{pl_?byDG)1G*vKyJWF5|g0wt%rzwbic11+(zM6o!@%Z@7(= zN+0Yq*g56mI+^CYQ}EH*!=qu7!{?A z+lVn1V(F%1AX?i2JgZr90Fu}(GKBmoczn}d{=Sf-t7%`vg!#LPnwu;f;Q z&>35($+9M^bTNFJz>@8% zLza~qb*Zq8z)(rK$Y=^NG=2=Z>qtvY;H!u@5{Q@ajR1mX&zaktU};Wfip(Sx*~%mU za+fs!ni_*89et|?L$S&oy>~YZZu`Xgbc#qt5nNw@YnkeI-4+~9kS_=xAi*DwiA@?|j zOPd5hlj<>n$#wdX!2u25H~AA|GeH%(z~Ux;z#UKQQnUC?9#+VmeCPq<`#D@xr{N=0^r8gFhzD&zBSQlMiX~1Z z*p$C0J3;bFl)DU13k6wg1jraX2~9)^D1(F`iDT`l{iUh@aH+sDL4nHjxlR0*j`ah@ z;)TVRKQT>!{$~7Oh6tFyB3_b4S?58Sp;+_~2l+aLG%~dyelUJpe*6k?DpbS>4c!dT zJI1D17$6?hGT_JW$V|AwQ;~`#l`5>AnE=0q53!1PmDESamDxA{c$`(I2IOT@NTB{P z-RSrPGG6^e9yH*A(UOZn1?-D;Tvt56l0|=5xK*v?;dkQv5kAJE9?Z+sHVfd$Oil{` zE-!)pn(u)ITA$p)#q#lm<~03LilW@!Ts^q<+ycT}1OLll1?xB09tQ&BV%N1r;+!yknb z9qM-wR2~0o9%W##eqvFFa9f-Is3u;;?&5yhI5>MfE$YBXkj=cA9u5tUj-v5}FR zwAKU}MH~rKyV#_mhClNvQEoHa{RbR|29SQ;0zmbOC+ zIts~EH!ac{z$Cln)&-VmwP|5b@$R#VRE$_PF3C#c(V$i3Klb(c8ZK$qxM&9CJYR0G3`2JQlsId1#4^IY$Y&WG^IIe^s$B_ z0^;j!6eFF)?2Fgjnhqbdg+LLsa=TT=oJG*_9Gt!-quh2IanagI^%*hnEsTuaf4iEF z>(l}2@pR4ZIMtXC>d#Eu`F3?^DWaCHHrsgcqNy7oY?kp4rgVBdY-YU%h=U*S?vya2N1N;v!NTSaG literal 0 HcmV?d00001 diff --git a/docs/api/errors/access-denied-error.rst b/docs/api/errors/access-denied-error.rst new file mode 100644 index 000000000..e561c5b0e --- /dev/null +++ b/docs/api/errors/access-denied-error.rst @@ -0,0 +1,82 @@ +=================== + AccessDeniedError +=================== + +The resource owner or authorization server denied the request. See :rfc:`Section 4.1.2.1 of RFC 6749 <6749#section-4.1.2.1>`. + +:: + + const AccessDeniedError = require('oauth2-server/lib/errors/access-denied-error'); + +-------- + +.. _AccessDeniedError#constructor: + +``new AccessDeniedError(message, properties)`` +============================================== + +Instantiates an ``AccessDeniedError``. + +**Arguments:** + ++-----------------------------------+--------------+-------------------------------------------------------------+ +| Name | Type | Description | ++===================================+==============+=============================================================+ +| [message=undefined] | String|Error | See :ref:`OAuthError#constructor`. | ++-----------------------------------+--------------+-------------------------------------------------------------+ +| [properties={}] | Object | See :ref:`OAuthError#constructor`. | ++-----------------------------------+--------------+-------------------------------------------------------------+ +| [properties.code=400] | Object | See :ref:`OAuthError#constructor`. | ++-----------------------------------+--------------+-------------------------------------------------------------+ +| [properties.name='access_denied'] | String | The error name used in responses generated from this error. | ++-----------------------------------+--------------+-------------------------------------------------------------+ + +**Return value:** + +A new instance of ``AccessDeniedError``. + +**Remarks:** + +:: + + const err = new AccessDeniedError(); + // err.message === 'Bad Request' + // err.code === 400 + // err.name === 'access_denied' + +-------- + +.. _AccessDeniedError#message: + +``message`` +=========== + +See :ref:`OAuthError#message `. + +-------- + +.. _AccessDeniedError#code: + +``code`` +======== + +Typically ``400``. See :ref:`OAuthError#code `. + +-------- + +.. _AccessDeniedError#inner: + +``inner`` +========= + +See :ref:`OAuthError#inner `. + +-------- + +.. _AccessDeniedError#name: + +``name`` +======== + +Typically ``'access_denied'``. See :ref:`OAuthError#name `. + diff --git a/docs/api/errors/index.rst b/docs/api/errors/index.rst new file mode 100644 index 000000000..f2aa79882 --- /dev/null +++ b/docs/api/errors/index.rst @@ -0,0 +1,34 @@ +======== + Errors +======== + +Noteable error types: + +``OAuthError`` + :doc:`oauth-error` is the base class for all exceptions thrown/returned by *oauth2-server*. + +``ServerError`` + :doc:`server-error` is used to wrap unknown exceptions encountered during request processing. + +``InvalidArgumentError`` + :doc:`invalid-argument-error` is thrown when an invalid argument is encountered. This error indicates that the module is used incorrectly and should never be seen because of external errors. + + +.. toctree:: + :maxdepth: 1 + :caption: Errors + :hidden: + + oauth-error + server-error + invalid-argument-error + access-denied-error + invalid-client-error + invalid-grant-error + invalid-request-error + invalid-scope-error + invalid-token-error + unauthorized-client-error + unauthorized-request-error + unsupported-grant-type-error + diff --git a/docs/api/errors/invalid-argument-error.rst b/docs/api/errors/invalid-argument-error.rst new file mode 100644 index 000000000..650e1d9f7 --- /dev/null +++ b/docs/api/errors/invalid-argument-error.rst @@ -0,0 +1,84 @@ +====================== + InvalidArgumentError +====================== + +An invalid argument was encountered. + +:: + + const InvalidArgumentError = require('oauth2-server/lib/errors/invalid-argument-error'); + +.. note:: This error indicates that the module is used incorrectly (i.e., there is a programming error) and should never be seen because of external errors (like invalid data sent by a client). + +-------- + +.. _InvalidArgumentError#constructor: + +``new InvalidArgumentError(message, properties)`` +================================================= + +Instantiates an ``InvalidArgumentError``. + +**Arguments:** + ++--------------------------------------+--------------+-------------------------------------------------------------+ +| Name | Type | Description | ++======================================+==============+=============================================================+ +| [message=undefined] | String|Error | See :ref:`OAuthError#constructor`. | ++--------------------------------------+--------------+-------------------------------------------------------------+ +| [properties={}] | Object | See :ref:`OAuthError#constructor`. | ++--------------------------------------+--------------+-------------------------------------------------------------+ +| [properties.code=500] | Object | See :ref:`OAuthError#constructor`. | ++--------------------------------------+--------------+-------------------------------------------------------------+ +| [properties.name='invalid_argument'] | String | The error name used in responses generated from this error. | ++--------------------------------------+--------------+-------------------------------------------------------------+ + +**Return value:** + +A new instance of ``InvalidArgumentError``. + +**Remarks:** + +:: + + const err = new InvalidArgumentError(); + // err.message === 'Internal Server Error' + // err.code === 500 + // err.name === 'invalid_argument' + +-------- + +.. _InvalidArgumentError#message: + +``message`` +=========== + +See :ref:`OAuthError#message `. + +-------- + +.. _InvalidArgumentError#code: + +``code`` +======== + +Typically ``500``. See :ref:`OAuthError#code `. + +-------- + +.. _InvalidArgumentError#inner: + +``inner`` +========= + +See :ref:`OAuthError#inner `. + +-------- + +.. _InvalidArgumentError#name: + +``name`` +======== + +Typically ``'invalid_argument'``. See :ref:`OAuthError#name `. + diff --git a/docs/api/errors/invalid-client-error.rst b/docs/api/errors/invalid-client-error.rst new file mode 100644 index 000000000..d25a49348 --- /dev/null +++ b/docs/api/errors/invalid-client-error.rst @@ -0,0 +1,82 @@ +==================== + InvalidClientError +==================== + +Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). See :rfc:`Section 5.2 of RFC 6749 <6749#section-5.2>`. + +:: + + const InvalidClientError = require('oauth2-server/lib/errors/invalid-client-error'); + +-------- + +.. _InvalidClientError#constructor: + +``new InvalidClientError(message, properties)`` +=============================================== + +Instantiates an ``InvalidClientError``. + +**Arguments:** + ++------------------------------------+--------------+-------------------------------------------------------------+ +| Name | Type | Description | ++====================================+==============+=============================================================+ +| [message=undefined] | String|Error | See :ref:`OAuthError#constructor`. | ++------------------------------------+--------------+-------------------------------------------------------------+ +| [properties={}] | Object | See :ref:`OAuthError#constructor`. | ++------------------------------------+--------------+-------------------------------------------------------------+ +| [properties.code=400] | Object | See :ref:`OAuthError#constructor`. | ++------------------------------------+--------------+-------------------------------------------------------------+ +| [properties.name='invalid_client'] | String | The error name used in responses generated from this error. | ++------------------------------------+--------------+-------------------------------------------------------------+ + +**Return value:** + +A new instance of ``InvalidClientError``. + +**Remarks:** + +:: + + const err = new InvalidClientError(); + // err.message === 'Bad Request' + // err.code === 400 + // err.name === 'invalid_client' + +-------- + +.. _InvalidClientError#message: + +``message`` +=========== + +See :ref:`OAuthError#message `. + +-------- + +.. _InvalidClientError#code: + +``code`` +======== + +Typically ``400``. See :ref:`OAuthError#code `. + +-------- + +.. _InvalidClientError#inner: + +``inner`` +========= + +See :ref:`OAuthError#inner `. + +-------- + +.. _InvalidClientError#name: + +``name`` +======== + +Typically ``'invalid_client'``. See :ref:`OAuthError#name `. + diff --git a/docs/api/errors/invalid-grant-error.rst b/docs/api/errors/invalid-grant-error.rst new file mode 100644 index 000000000..8f2a9ba23 --- /dev/null +++ b/docs/api/errors/invalid-grant-error.rst @@ -0,0 +1,82 @@ +=================== + InvalidGrantError +=================== + +The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client. See :rfc:`Section 5.2 of RFC 6749 <6749#section-5.2>`. + +:: + + const InvalidGrantError = require('oauth2-server/lib/errors/invalid-grant-error'); + +-------- + +.. _InvalidGrantError#constructor: + +``new InvalidGrantError(message, properties)`` +============================================== + +Instantiates an ``InvalidGrantError``. + +**Arguments:** + ++-----------------------------------+--------------+-------------------------------------------------------------+ +| Name | Type | Description | ++===================================+==============+=============================================================+ +| [message=undefined] | String|Error | See :ref:`OAuthError#constructor`. | ++-----------------------------------+--------------+-------------------------------------------------------------+ +| [properties={}] | Object | See :ref:`OAuthError#constructor`. | ++-----------------------------------+--------------+-------------------------------------------------------------+ +| [properties.code=400] | Object | See :ref:`OAuthError#constructor`. | ++-----------------------------------+--------------+-------------------------------------------------------------+ +| [properties.name='invalid_grant'] | String | The error name used in responses generated from this error. | ++-----------------------------------+--------------+-------------------------------------------------------------+ + +**Return value:** + +A new instance of ``InvalidGrantError``. + +**Remarks:** + +:: + + const err = new InvalidGrantError(); + // err.message === 'Bad Request' + // err.code === 400 + // err.name === 'invalid_grant' + +-------- + +.. _InvalidGrantError#message: + +``message`` +=========== + +See :ref:`OAuthError#message `. + +-------- + +.. _InvalidGrantError#code: + +``code`` +======== + +Typically ``400``. See :ref:`OAuthError#code `. + +-------- + +.. _InvalidGrantError#inner: + +``inner`` +========= + +See :ref:`OAuthError#inner `. + +-------- + +.. _InvalidGrantError#name: + +``name`` +======== + +Typically ``'invalid_grant'``. See :ref:`OAuthError#name `. + diff --git a/docs/api/errors/invalid-request-error.rst b/docs/api/errors/invalid-request-error.rst new file mode 100644 index 000000000..119ab40ee --- /dev/null +++ b/docs/api/errors/invalid-request-error.rst @@ -0,0 +1,82 @@ +===================== + InvalidRequestError +===================== + +The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. See :rfc:`Section 4.2.2.1 of RFC 6749 <6749#section-4.2.2.1>`. + +:: + + const InvalidRequestError = require('oauth2-server/lib/errors/invalid-request-error'); + +-------- + +.. _InvalidRequestError#constructor: + +``new InvalidRequestError(message, properties)`` +================================================ + +Instantiates an ``InvalidRequestError``. + +**Arguments:** + ++-------------------------------------+--------------+-------------------------------------------------------------+ +| Name | Type | Description | ++=====================================+==============+=============================================================+ +| [message=undefined] | String|Error | See :ref:`OAuthError#constructor`. | ++-------------------------------------+--------------+-------------------------------------------------------------+ +| [properties={}] | Object | See :ref:`OAuthError#constructor`. | ++-------------------------------------+--------------+-------------------------------------------------------------+ +| [properties.code=400] | Object | See :ref:`OAuthError#constructor`. | ++-------------------------------------+--------------+-------------------------------------------------------------+ +| [properties.name='invalid_request'] | String | The error name used in responses generated from this error. | ++-------------------------------------+--------------+-------------------------------------------------------------+ + +**Return value:** + +A new instance of ``InvalidRequestError``. + +**Remarks:** + +:: + + const err = new InvalidRequestError(); + // err.message === 'Bad Request' + // err.code === 400 + // err.name === 'invalid_request' + +-------- + +.. _InvalidRequestError#message: + +``message`` +=========== + +See :ref:`OAuthError#message `. + +-------- + +.. _InvalidRequestError#code: + +``code`` +======== + +Typically ``400``. See :ref:`OAuthError#code `. + +-------- + +.. _InvalidRequestError#inner: + +``inner`` +========= + +See :ref:`OAuthError#inner `. + +-------- + +.. _InvalidRequestError#name: + +``name`` +======== + +Typically ``'invalid_request'``. See :ref:`OAuthError#name `. + diff --git a/docs/api/errors/invalid-scope-error.rst b/docs/api/errors/invalid-scope-error.rst new file mode 100644 index 000000000..801930f96 --- /dev/null +++ b/docs/api/errors/invalid-scope-error.rst @@ -0,0 +1,82 @@ +=================== + InvalidScopeError +=================== + +The requested scope is invalid, unknown, or malformed. See :rfc:`Section 4.1.2.1 of RFC 6749 <6749#section-4.1.2.1>`. + +:: + + const InvalidScopeError = require('oauth2-server/lib/errors/invalid-scope-error'); + +-------- + +.. _InvalidScopeError#constructor: + +``new InvalidScopeError(message, properties)`` +============================================== + +Instantiates an ``InvalidScopeError``. + +**Arguments:** + ++-----------------------------------+--------------+-------------------------------------------------------------+ +| Name | Type | Description | ++===================================+==============+=============================================================+ +| [message=undefined] | String|Error | See :ref:`OAuthError#constructor`. | ++-----------------------------------+--------------+-------------------------------------------------------------+ +| [properties={}] | Object | See :ref:`OAuthError#constructor`. | ++-----------------------------------+--------------+-------------------------------------------------------------+ +| [properties.code=400] | Object | See :ref:`OAuthError#constructor`. | ++-----------------------------------+--------------+-------------------------------------------------------------+ +| [properties.name='invalid_scope'] | String | The error name used in responses generated from this error. | ++-----------------------------------+--------------+-------------------------------------------------------------+ + +**Return value:** + +A new instance of ``InvalidScopeError``. + +**Remarks:** + +:: + + const err = new InvalidScopeError(); + // err.message === 'Bad Request' + // err.code === 400 + // err.name === 'invalid_scope' + +-------- + +.. _InvalidScopeError#message: + +``message`` +=========== + +See :ref:`OAuthError#message `. + +-------- + +.. _InvalidScopeError#code: + +``code`` +======== + +Typically ``400``. See :ref:`OAuthError#code `. + +-------- + +.. _InvalidScopeError#inner: + +``inner`` +========= + +See :ref:`OAuthError#inner `. + +-------- + +.. _InvalidScopeError#name: + +``name`` +======== + +Typically ``'invalid_scope'``. See :ref:`OAuthError#name `. + diff --git a/docs/api/errors/invalid-token-error.rst b/docs/api/errors/invalid-token-error.rst new file mode 100644 index 000000000..21ffad8f5 --- /dev/null +++ b/docs/api/errors/invalid-token-error.rst @@ -0,0 +1,82 @@ +=================== + InvalidTokenError +=================== + +The access token provided is expired, revoked, malformed, or invalid for other reasons. See :rfc:`Section 3.1 of RFC 6750 <6750#section-3.1>`. + +:: + + const InvalidTokenError = require('oauth2-server/lib/errors/invalid-token-error'); + +-------- + +.. _InvalidTokenError#constructor: + +``new InvalidTokenError(message, properties)`` +============================================== + +Instantiates an ``InvalidTokenError``. + +**Arguments:** + ++-----------------------------------+--------------+-------------------------------------------------------------+ +| Name | Type | Description | ++===================================+==============+=============================================================+ +| [message=undefined] | String|Error | See :ref:`OAuthError#constructor`. | ++-----------------------------------+--------------+-------------------------------------------------------------+ +| [properties={}] | Object | See :ref:`OAuthError#constructor`. | ++-----------------------------------+--------------+-------------------------------------------------------------+ +| [properties.code=401] | Object | See :ref:`OAuthError#constructor`. | ++-----------------------------------+--------------+-------------------------------------------------------------+ +| [properties.name='invalid_token'] | String | The error name used in responses generated from this error. | ++-----------------------------------+--------------+-------------------------------------------------------------+ + +**Return value:** + +A new instance of ``InvalidTokenError``. + +**Remarks:** + +:: + + const err = new InvalidTokenError(); + // err.message === 'Unauthorized' + // err.code === 401 + // err.name === 'invalid_token' + +-------- + +.. _InvalidTokenError#message: + +``message`` +=========== + +See :ref:`OAuthError#message `. + +-------- + +.. _InvalidTokenError#code: + +``code`` +======== + +Typically ``401``. See :ref:`OAuthError#code `. + +-------- + +.. _InvalidTokenError#inner: + +``inner`` +========= + +See :ref:`OAuthError#inner `. + +-------- + +.. _InvalidTokenError#name: + +``name`` +======== + +Typically ``'invalid_token'``. See :ref:`OAuthError#name `. + diff --git a/docs/api/errors/oauth-error.rst b/docs/api/errors/oauth-error.rst new file mode 100644 index 000000000..c7f1d8617 --- /dev/null +++ b/docs/api/errors/oauth-error.rst @@ -0,0 +1,124 @@ +============ + OAuthError +============ + +Base class for all errors returned by this module. + +:: + + const OAuthError = require('oauth2-server/lib/errors/oauth-error'); + +-------- + +.. _OAuthError#constructor: + +``new OAuthError(message, properties)`` +======================================= + +Instantiates ``OAuthError``. + +.. note:: Do not use ``OAuthError`` directly; it's intended to be used as a base class. Instead, use one of the other error types derived from it. + +**Arguments:** + ++-----------------------------+--------------+------------------------------------------------------------------------------------+ +| Name | Type | Description | ++=============================+==============+====================================================================================+ +| [message=undefined] | String|Error | Error message or nested exception. | ++-----------------------------+--------------+------------------------------------------------------------------------------------+ +| [properties={}] | Object | Additional properties to be set on the error object. | ++-----------------------------+--------------+------------------------------------------------------------------------------------+ +| [properties.code=500] | Object | An HTTP status code associated with the error. | ++-----------------------------+--------------+------------------------------------------------------------------------------------+ +| [properties.name=undefined] | String | The name of the error. If left undefined, the name is copied from the constructor. | ++-----------------------------+--------------+------------------------------------------------------------------------------------+ + +**Return value:** + +A new instance of ``OAuthError``. + +**Remarks:** + +By default ``code`` is set to ``500`` and ``message`` is set to the respective HTTP phrase. + +:: + + const err = new OAuthError(); + // err.message === 'Internal Server Error' + // err.code === 500 + // err.name === 'OAuthError' + +:: + + const err = new OAuthError('test', {name: 'test_error'}); + // err.message === 'test' + // err.code === 500 + // err.name === 'test_error' + +:: + + const err = new OAuthError(undefined, {code: 404}); + // err.message === 'Not Found' + // err.code === 404 + // err.name === 'OAuthError' + +All additional ``properties`` are copied to the error object. + +:: + + const err = new OAuthError('test', {foo: 'bar', baz: 1234}); + // err.message === 'test' + // err.code === 500 + // err.name === 'OAuthError' + // err.foo === 'bar' + // err.baz === 1234 + +When wrapping an exception, the ``message`` property is automatically copied from the existing exception. + +:: + + const anotherError = new Error('test'); + const err = new OAuthError(e); + // err.message === 'test' + // err.code === 500 + // err.name === 'OAuthError' + // err.inner === anotherError + +-------- + +.. _OAuthError#message: + +``message`` +=========== + +A message describing the error. + +-------- + +.. _OAuthError#code: + +``code`` +======== + +An HTTP status code associated with the error. + +For compatibility reasons, two more properties exist that have the same value as ``code``: ``status`` and ``statusCode``. Note that changes to one of these are not reflected by the other properties. + +-------- + +.. _OAuthError#inner: + +``inner`` +========= + +Another exception that was wrapped by this ``OAuthError`` instance. This property is set only if the error is constructed from an existing exception. + +-------- + +.. _OAuthError#name: + +``name`` +======== + +The name of the error, intended to be used as the ``error`` parameter as described by :rfc:`6749` in :rfc:`Section 4.1.2.1 <6749#section-4.1.2.1>`, :rfc:`Section 4.2.2.1 <6749#section-4.2.2.1>` and :rfc:`Section 5.2 <6749#section-5.2>`, as well as :rfc:`Section 3.1 of RFC 6750 <6750#section-3.1>`. + diff --git a/docs/api/errors/server-error.rst b/docs/api/errors/server-error.rst new file mode 100644 index 000000000..13f436ed6 --- /dev/null +++ b/docs/api/errors/server-error.rst @@ -0,0 +1,84 @@ +============= + ServerError +============= + +The authorization server encountered an unexpected condition that prevented it from fulfilling the request. See :rfc:`Section 4.1.2.1 of RFC 6749 <6749#section-4.1.2.1>`. + +:: + + const ServerError = require('oauth2-server/lib/errors/server-error'); + +``ServerError`` is used to wrap unknown exceptions encountered during request processing. + +-------- + +.. _ServerError#constructor: + +``new ServerError(message, properties)`` +======================================== + +Instantiates an ``ServerError``. + +**Arguments:** + ++----------------------------------+--------------+-------------------------------------------------------------+ +| Name | Type | Description | ++==================================+==============+=============================================================+ +| [message=undefined] | String|Error | See :ref:`OAuthError#constructor`. | ++----------------------------------+--------------+-------------------------------------------------------------+ +| [properties={}] | Object | See :ref:`OAuthError#constructor`. | ++----------------------------------+--------------+-------------------------------------------------------------+ +| [properties.code=503] | Object | See :ref:`OAuthError#constructor`. | ++----------------------------------+--------------+-------------------------------------------------------------+ +| [properties.name='server_error'] | String | The error name used in responses generated from this error. | ++----------------------------------+--------------+-------------------------------------------------------------+ + +**Return value:** + +A new instance of ``ServerError``. + +**Remarks:** + +:: + + const err = new ServerError(); + // err.message === 'Service Unavailable Error' + // err.code === 503 + // err.name === 'server_error' + +-------- + +.. _ServerError#message: + +``message`` +=========== + +See :ref:`OAuthError#message `. + +-------- + +.. _ServerError#code: + +``code`` +======== + +Typically ``503``. See :ref:`OAuthError#code `. + +-------- + +.. _ServerError#inner: + +``inner`` +========= + +See :ref:`OAuthError#inner `. + +-------- + +.. _ServerError#name: + +``name`` +======== + +Typically ``'server_error'``. See :ref:`OAuthError#name `. + diff --git a/docs/api/errors/unauthorized-client-error.rst b/docs/api/errors/unauthorized-client-error.rst new file mode 100644 index 000000000..d04cb0800 --- /dev/null +++ b/docs/api/errors/unauthorized-client-error.rst @@ -0,0 +1,82 @@ +========================= + UnauthorizedClientError +========================= + +The authenticated client is not authorized to use this authorization grant type. See :rfc:`Section 4.1.2.1 of RFC 6749 <6749#section-4.1.2.1>`. + +:: + + const UnauthorizedClientError = require('oauth2-server/lib/errors/unauthorized-client-error'); + +-------- + +.. _UnauthorizedClientError#constructor: + +``new UnauthorizedClientError(message, properties)`` +==================================================== + +Instantiates an ``UnauthorizedClientError``. + +**Arguments:** + ++-----------------------------------------+--------------+-------------------------------------------------------------+ +| Name | Type | Description | ++=========================================+==============+=============================================================+ +| [message=undefined] | String|Error | See :ref:`OAuthError#constructor`. | ++-----------------------------------------+--------------+-------------------------------------------------------------+ +| [properties={}] | Object | See :ref:`OAuthError#constructor`. | ++-----------------------------------------+--------------+-------------------------------------------------------------+ +| [properties.code=400] | Object | See :ref:`OAuthError#constructor`. | ++-----------------------------------------+--------------+-------------------------------------------------------------+ +| [properties.name='unauthorized_client'] | String | The error name used in responses generated from this error. | ++-----------------------------------------+--------------+-------------------------------------------------------------+ + +**Return value:** + +A new instance of ``UnauthorizedClientError``. + +**Remarks:** + +:: + + const err = new UnauthorizedClientError(); + // err.message === 'Bad Request' + // err.code === 400 + // err.name === 'unauthorized_client' + +-------- + +.. _UnauthorizedClientError#message: + +``message`` +=========== + +See :ref:`OAuthError#message `. + +-------- + +.. _UnauthorizedClientError#code: + +``code`` +======== + +Typically ``400``. See :ref:`OAuthError#code `. + +-------- + +.. _UnauthorizedClientError#inner: + +``inner`` +========= + +See :ref:`OAuthError#inner `. + +-------- + +.. _UnauthorizedClientError#name: + +``name`` +======== + +Typically ``'unauthorized_client'``. See :ref:`OAuthError#name `. + diff --git a/docs/api/errors/unauthorized-request-error.rst b/docs/api/errors/unauthorized-request-error.rst new file mode 100644 index 000000000..495f5f8c3 --- /dev/null +++ b/docs/api/errors/unauthorized-request-error.rst @@ -0,0 +1,89 @@ +========================== + UnauthorizedRequestError +========================== + +The request lacked any authentication information or the client attempted to use an unsupported authentication method. + +:: + + const UnauthorizedRequestError = require('oauth2-server/lib/errors/unauthorized-request-error'); + +According to :rfc:`Section 3.1 of RFC 6750 <6750#section-3.1>` you should just fail the request with ``401 Unauthorized`` and not send any error information in the body if this error occurs: + + If the request lacks any authentication information (e.g., the client + was unaware that authentication is necessary or attempted using an + unsupported authentication method), the resource server SHOULD NOT + include an error code or other error information. + +-------- + +.. _UnauthorizedRequestError#constructor: + +``new UnauthorizedRequestError(message, properties)`` +===================================================== + +Instantiates an ``UnauthorizedRequestError``. + +**Arguments:** + ++------------------------------------------+--------------+-------------------------------------------------------------+ +| Name | Type | Description | ++==========================================+==============+=============================================================+ +| [message=undefined] | String|Error | See :ref:`OAuthError#constructor`. | ++------------------------------------------+--------------+-------------------------------------------------------------+ +| [properties={}] | Object | See :ref:`OAuthError#constructor`. | ++------------------------------------------+--------------+-------------------------------------------------------------+ +| [properties.code=401] | Object | See :ref:`OAuthError#constructor`. | ++------------------------------------------+--------------+-------------------------------------------------------------+ +| [properties.name='unauthorized_request'] | String | The error name used in responses generated from this error. | ++------------------------------------------+--------------+-------------------------------------------------------------+ + +**Return value:** + +A new instance of ``UnauthorizedRequestError``. + +**Remarks:** + +:: + + const err = new UnauthorizedRequestError(); + // err.message === 'Unauthorized' + // err.code === 401 + // err.name === 'unauthorized_request' + +-------- + +.. _UnauthorizedRequestError#message: + +``message`` +=========== + +See :ref:`OAuthError#message `. + +-------- + +.. _UnauthorizedRequestError#code: + +``code`` +======== + +Typically ``401``. See :ref:`OAuthError#code `. + +-------- + +.. _UnauthorizedRequestError#inner: + +``inner`` +========= + +See :ref:`OAuthError#inner `. + +-------- + +.. _UnauthorizedRequestError#name: + +``name`` +======== + +Typically ``'unauthorized_request'``. See :ref:`OAuthError#name `. + diff --git a/docs/api/errors/unsupported-grant-type-error.rst b/docs/api/errors/unsupported-grant-type-error.rst new file mode 100644 index 000000000..d2fe49f7e --- /dev/null +++ b/docs/api/errors/unsupported-grant-type-error.rst @@ -0,0 +1,82 @@ +=========================== + UnsupportedGrantTypeError +=========================== + +The authorization grant type is not supported by the authorization server. See :rfc:`Section 4.1.2.1 of RFC 6749 <6749#section-4.1.2.1>`. + +:: + + const UnsupportedGrantTypeError = require('oauth2-server/lib/errors/unsupported-grant-type-error'); + +-------- + +.. _UnsupportedGrantTypeError#constructor: + +``new UnsupportedGrantTypeError(message, properties)`` +====================================================== + +Instantiates an ``UnsupportedGrantTypeError``. + +**Arguments:** + ++--------------------------------------------+--------------+-------------------------------------------------------------+ +| Name | Type | Description | ++============================================+==============+=============================================================+ +| [message=undefined] | String|Error | See :ref:`OAuthError#constructor`. | ++--------------------------------------------+--------------+-------------------------------------------------------------+ +| [properties={}] | Object | See :ref:`OAuthError#constructor`. | ++--------------------------------------------+--------------+-------------------------------------------------------------+ +| [properties.code=400] | Object | See :ref:`OAuthError#constructor`. | ++--------------------------------------------+--------------+-------------------------------------------------------------+ +| [properties.name='unsupported_grant_type'] | String | The error name used in responses generated from this error. | ++--------------------------------------------+--------------+-------------------------------------------------------------+ + +**Return value:** + +A new instance of ``UnsupportedGrantTypeError``. + +**Remarks:** + +:: + + const err = new UnsupportedGrantTypeError(); + // err.message === 'Bad Request' + // err.code === 400 + // err.name === 'unsupported_grant_type' + +-------- + +.. _UnsupportedGrantTypeError#message: + +``message`` +=========== + +See :ref:`OAuthError#message `. + +-------- + +.. _UnsupportedGrantTypeError#code: + +``code`` +======== + +Typically ``400``. See :ref:`OAuthError#code `. + +-------- + +.. _UnsupportedGrantTypeError#inner: + +``inner`` +========= + +See :ref:`OAuthError#inner `. + +-------- + +.. _UnsupportedGrantTypeError#name: + +``name`` +======== + +Typically ``'unsupported_grant_type'``. See :ref:`OAuthError#name `. + diff --git a/docs/api/oauth2-server.rst b/docs/api/oauth2-server.rst new file mode 100644 index 000000000..b2dd784fd --- /dev/null +++ b/docs/api/oauth2-server.rst @@ -0,0 +1,294 @@ +============== + OAuth2Server +============== + +Represents an OAuth2 server instance. + +:: + + const OAuth2Server = require('oauth2-server'); + +-------- + +.. _OAuth2Server#constructor: + +``new OAuth2Server(options)`` +============================= + +Instantiates ``OAuth2Server`` using the supplied model. + +**Arguments:** + ++---------------+--------+---------------------------------+ +| Name | Type | Description | ++===============+========+=================================+ +| options | Object | Server options. | ++---------------+--------+---------------------------------+ +| options.model | Object | The :doc:`Model `. | ++---------------+--------+---------------------------------+ + +**Return value:** + +A new ``OAuth2Server`` instance. + +**Remarks:** + +Any valid option for :ref:`OAuth2Server#authenticate() `, :ref:`OAuth2Server#authorize() ` and :ref:`OAuth2Server#token() ` can be passed to the constructor as well. The supplied options will be used as default for the other methods. + +Basic usage: + +:: + + const oauth = new OAuth2Server({ + model: require('./model') + }); + +Advanced example with additional options: + +:: + + const oauth = new OAuth2Server({ + model: require('./model'), + allowBearerTokensInQueryString: true, + accessTokenLifetime: 4 * 60 * 60 + }); + +-------- + +.. _OAuth2Server#authenticate: + +``authenticate(request, response, [options], [callback])`` +========================================================== + +Authenticates a request. + +**Arguments:** + ++------------------------------------------------+-----------------+-----------------------------------------------------------------------+ +| Name | Type | Description | ++================================================+=================+=======================================================================+ +| request | :doc:`request` | Request object. | ++------------------------------------------------+-----------------+-----------------------------------------------------------------------+ +| response | :doc:`response` | Response object. | ++------------------------------------------------+-----------------+-----------------------------------------------------------------------+ +| [options={}] | Object | Handler options. | ++------------------------------------------------+-----------------+-----------------------------------------------------------------------+ +| [options.scope=undefined] | String | The scope(s) to authenticate. | ++------------------------------------------------+-----------------+-----------------------------------------------------------------------+ +| [options.addAcceptedScopesHeader=true] | Boolean | Set the ``X-Accepted-OAuth-Scopes`` HTTP header on response objects. | ++------------------------------------------------+-----------------+-----------------------------------------------------------------------+ +| [options.addAuthorizedScopesHeader=true] | Boolean | Set the ``X-OAuth-Scopes`` HTTP header on response objects. | ++------------------------------------------------+-----------------+-----------------------------------------------------------------------+ +| [options.allowBearerTokensInQueryString=false] | Boolean | Allow clients to pass bearer tokens in the query string of a request. | ++------------------------------------------------+-----------------+-----------------------------------------------------------------------+ +| [callback=undefined] | Function | Node-style callback to be used instead of the returned ``Promise``. | ++------------------------------------------------+-----------------+-----------------------------------------------------------------------+ + +**Return value:** + +A ``Promise`` that resolves to the access token object returned from :ref:`Model#getAccessToken() `. +In case of an error, the promise rejects with one of the error types derived from :doc:`/api/errors/oauth-error`. + +Possible errors include but are not limited to: + +:doc:`/api/errors/unauthorized-request-error`: + The protected resource request failed authentication. + +The returned ``Promise`` **must** be ignored if ``callback`` is used. + +**Remarks:** + +:: + + const oauth = new OAuth2Server({model: ...}); + + function authenticateHandler(options) { + return function(req, res, next) { + let request = new Request(req); + let response = new Response(res); + return oauth.authenticate(request, response, options) + .then(function(token) { + res.locals.oauth = {token: token}; + next(); + }) + .catch(function(err) { + // handle error condition + }); + } + } + +-------- + +.. _OAuth2Server#authorize: + +``authorize(request, response, [options], [callback])`` +======================================================= + +Authorizes a token request. + +**Arguments:** + ++-----------------------------------------+-----------------+-----------------------------------------------------------------------------+ +| Name | Type | Description | ++=========================================+=================+=============================================================================+ +| request | :doc:`request` | Request object. | ++-----------------------------------------+-----------------+-----------------------------------------------------------------------------+ +| [request.query.allowed=undefined] | String | ``'false'`` to deny the authorization request (see remarks section). | ++-----------------------------------------+-----------------+-----------------------------------------------------------------------------+ +| response | :doc:`response` | Response object. | ++-----------------------------------------+-----------------+-----------------------------------------------------------------------------+ +| [options={}] | Object | Handler options. | ++-----------------------------------------+-----------------+-----------------------------------------------------------------------------+ +| [options.authenticateHandler=undefined] | Object | The authenticate handler (see remarks section). | ++-----------------------------------------+-----------------+-----------------------------------------------------------------------------+ +| [options.allowEmptyState=false] | Boolean | Allow clients to specify an empty ``state``. | ++-----------------------------------------+-----------------+-----------------------------------------------------------------------------+ +| [options.authorizationCodeLifetime=300] | Number | Lifetime of generated authorization codes in seconds (default = 5 minutes). | ++-----------------------------------------+-----------------+-----------------------------------------------------------------------------+ +| [callback=undefined] | Function | Node-style callback to be used instead of the returned ``Promise``. | ++-----------------------------------------+-----------------+-----------------------------------------------------------------------------+ + +**Return value:** + +A ``Promise`` that resolves to the authorization code object returned from :ref:`Model#saveAuthorizationCode() `. +In case of an error, the promise rejects with one of the error types derived from :doc:`/api/errors/oauth-error`. + +Possible errors include but are not limited to: + +:doc:`/api/errors/access-denied-error` + The resource owner denied the access request (i.e. ``request.query.allow`` was ``'false'``). + +The returned ``Promise`` **must** be ignored if ``callback`` is used. + +**Remarks:** + +If ``request.query.allowed`` equals the string ``'false'`` the access request is denied and the returned promise is rejected with an :doc:`/api/errors/access-denied-error`. + +In order to retrieve the user associated with the request, ``options.authenticateHandler`` should be supplied. +The ``authenticateHandler`` has to be an object implementing a ``handle(request, response)`` function that returns a user object. +If there is no associated user (i.e. the user is not logged in) a falsy value should be returned. + +:: + + let authenticateHandler = { + handle: function(request, response) { + return /* get authenticated user */; + } + }; + +When working with a session-based login mechanism, the handler can simply look like this: + +:: + + let authenticateHandler = { + handle: function(request, response) { + return request.session.user; + } + }; + +.. todo:: Move ``authenticateHandler`` to it's own section. + +:: + + const oauth = new OAuth2Server({model: ...}); + + function authorizeHandler(options) { + return function(req, res, next) { + let request = new Request(req); + let response = new Response(res); + return oauth.authorize(request, response, options) + .then(function(code) { + res.locals.oauth = {code: code}; + next(); + }) + .catch(function(err) { + // handle error condition + }); + } + } + +-------- + +.. _OAuth2Server#token: + +``token(request, response, [options], [callback])`` +=================================================== + +Retrieves a new token for an authorized token request. + +**Arguments:** + ++----------------------------------------------+-----------------+-------------------------------------------------------------------------------------------+ +| Name | Type | Description | ++==============================================+=================+===========================================================================================+ +| request | :doc:`request` | Request object. | ++----------------------------------------------+-----------------+-------------------------------------------------------------------------------------------+ +| response | :doc:`response` | Response object. | ++----------------------------------------------+-----------------+-------------------------------------------------------------------------------------------+ +| [options={}] | Object | Handler options. | ++----------------------------------------------+-----------------+-------------------------------------------------------------------------------------------+ +| [options.accessTokenLifetime=3600] | Number | Lifetime of generated access tokens in seconds (default = 1 hour). | ++----------------------------------------------+-----------------+-------------------------------------------------------------------------------------------+ +| [options.refreshTokenLifetime=1209600] | Number | Lifetime of generated refresh tokens in seconds (default = 2 weeks). | ++----------------------------------------------+-----------------+-------------------------------------------------------------------------------------------+ +| [options.allowExtendedTokenAttributes=false] | Boolean | Allow extended attributes to be set on the returned token (see remarks section). | ++----------------------------------------------+-----------------+-------------------------------------------------------------------------------------------+ +| [options.requiresClientAuthentication={}] | Object | Require a client secret (see remarks section). Defaults to ``true`` for all grant types. | ++----------------------------------------------+-----------------+-------------------------------------------------------------------------------------------+ +| [options.alwaysIssueNewRefreshToken=true] | Boolean | Always revoke the used refresh token and issue a new one for the ``refresh_token`` grant. | ++----------------------------------------------+-----------------+-------------------------------------------------------------------------------------------+ +| [options.extendedGrantTypes={}] | Object | Additional supported grant types. | ++----------------------------------------------+-----------------+-------------------------------------------------------------------------------------------+ +| [callback=undefined] | Function | Node-style callback to be used instead of the returned ``Promise``. | ++----------------------------------------------+-----------------+-------------------------------------------------------------------------------------------+ + +**Return value:** + +A ``Promise`` that resolves to the token object returned from :ref:`Model#saveToken() `. +In case of an error, the promise rejects with one of the error types derived from :doc:`/api/errors/oauth-error`. + +Possible errors include but are not limited to: + +:doc:`/api/errors/invalid-grant-error`: + The access token request was invalid or not authorized. + +The returned ``Promise`` **must** be ignored if ``callback`` is used. + +**Remarks:** + +If ``options.allowExtendedTokenAttributes`` is ``true`` any additional properties set on the object returned from :ref:`Model#saveToken() ` are copied to the token response sent to the client. + +.. todo:: ``options.requiresClientAuthentication`` + +``options.extendedGrantTypes`` is an object mapping extension grant URIs to handler types, for example: + +:: + + let options = { + // ... + extendedGrantTypes: { + 'urn:foo:bar:baz': MyGrantType + } + }; + +For information on how to implement a handler for a custom grant type see :doc:`/misc/extension-grants`. + +:: + + const oauth = new OAuth2Server({model: ...}); + + function tokenHandler(options) { + return function(req, res, next) { + let request = new Request(req); + let response = new Response(res); + return oauth.token(request, response, options) + .then(function(code) { + res.locals.oauth = {token: token}; + next(); + }) + .catch(function(err) { + // handle error condition + }); + } + } + diff --git a/docs/api/request.rst b/docs/api/request.rst new file mode 100644 index 000000000..b8f8963a2 --- /dev/null +++ b/docs/api/request.rst @@ -0,0 +1,134 @@ +========= + Request +========= + +Represents an incoming HTTP request. + +:: + + const Request = require('oauth2-server').Request; + +-------- + +.. _Request#constructor: + +``new Request(options)`` +======================== + +Instantiates ``Request`` using the supplied options. + +**Arguments:** + ++-------------------+--------+--------------------------------------------------------+ +| Name | Type | Description | ++===================+========+========================================================+ +| options | Object | Request options. | ++-------------------+--------+--------------------------------------------------------+ +| options.method | String | The HTTP method of the request. | ++-------------------+--------+--------------------------------------------------------+ +| options.query | Object | The request's query string parameters. | ++-------------------+--------+--------------------------------------------------------+ +| options.headers | Object | The request's HTTP header fields. | ++-------------------+--------+--------------------------------------------------------+ +| [options.body={}] | Object | Key-value pairs of data submitted in the request body. | ++-------------------+--------+--------------------------------------------------------+ + +All additional own properties are copied to the new ``Request`` object as well. + +**Return value:** + +A new ``Request`` instance. + +**Remarks:** + +The names of HTTP header fields passed in as ``options.headers`` are converted to lower case. + +To convert `Express' request`_ to a ``Request`` simply pass ``req`` as ``options``: + +.. _Express' request: https://expressjs.com/en/4x/api.html#req + +:: + + function(req, res, next) { + var request = new Request(req); + // ... + } + +-------- + +.. _Request#get: + +``get(field)`` +============== + +Returns the specified HTTP header field. The match is case-insensitive. + +**Arguments:** + ++-------+--------+------------------------+ +| Name | Type | Description | ++=======+========+========================+ +| field | String | The header field name. | ++-------+--------+------------------------+ + +**Return value:** + +The value of the header field or ``undefined`` if the field does not exist. + +-------- + +.. _Request#is: + +``is(types)`` +============= + +Checks if the request's ``Content-Type`` HTTP header matches any of the given MIME types. + +**Arguments:** + ++-------+----------------------+-----------------------------------+ +| Name | Type | Description | ++=======+======================+===================================+ +| types | Array|String | The MIME type(s) to test against. | ++-------+----------------------+-----------------------------------+ + +**Return value:** + +Returns the matching MIME type or ``false`` if there was no match. + +-------- + +.. _Request#method: + +``method`` +========== + +The HTTP method of the request (``'GET'``, ``'POST'``, ``'PUT'``, ...). + +-------- + +.. _Request#query: + +``query`` +========= + +The request's query string parameters. + +-------- + +.. _Request#headers: + +``headers`` +=========== + +The request's HTTP header fields. Prefer :ref:`Request#get() ` over accessing this object directly. + +-------- + +.. _Request#body: + +``body`` +======== + +Key-value pairs of data submitted in the request body. + diff --git a/docs/api/response.rst b/docs/api/response.rst new file mode 100644 index 000000000..48cc36dc0 --- /dev/null +++ b/docs/api/response.rst @@ -0,0 +1,148 @@ +========== + Response +========== + +Represents an outgoing HTTP response. + +:: + + const Response = require('oauth2-server').Response; + +-------- + +.. _Response#constructor: + +``new Response(options)`` +========================= + +Instantiates ``Response`` using the supplied options. + +**Arguments:** + ++-------------------+--------+---------------------------------------------------------------+ +| Name | Type | Description | ++===================+========+===============================================================+ +| options | Object | Response options. | ++-------------------+--------+---------------------------------------------------------------+ +| options.headers | Object | The response's HTTP header fields. | ++-------------------+--------+---------------------------------------------------------------+ +| [options.body={}] | Object | Key-value pairs of data to be submitted in the response body. | ++-------------------+--------+---------------------------------------------------------------+ + +All additional own properties are copied to the new ``Response`` object as well. + +**Return value:** + +A new ``Response`` instance. + +**Remarks:** + +The names of HTTP header fields passed in as ``options.headers`` are converted to lower case. + +To convert `Express' response`_ to a ``Response`` simply pass ``res`` as ``options``: + +.. _Express' response: https://expressjs.com/en/4x/api.html#res + +:: + + function(req, res, next) { + var response = new Response(res); + // ... + } + +-------- + +.. _Response#get: + +``get(field)`` +============== + +Returns the specified HTTP header field. The match is case-insensitive. + +**Arguments:** + ++-------+--------+------------------------+ +| Name | Type | Description | ++=======+========+========================+ +| field | String | The header field name. | ++-------+--------+------------------------+ + +**Return value:** + +The value of the header field or ``undefined`` if the field does not exist. + +-------- + +.. _Response#set: + +``set(field, value)`` +===================== + +Sets the specified HTTP header field. The match is case-insensitive. + +**Arguments:** + ++-------+--------+-------------------------+ +| Name | Type | Description | ++=======+========+=========================+ +| field | String | The header field name. | ++-------+--------+-------------------------+ +| value | String | The header field value. | ++-------+--------+-------------------------+ + +**Return value:** + +None. + +-------- + +.. _Response#redirect: + +``redirect(url)`` +================= + +Redirects to the specified URL using ``302 Found``. + +**Arguments:** + ++------+--------+-------------------------+ +| Name | Type | Description | ++======+========+=========================+ +| url | String | The URL to redirect to. | ++------+--------+-------------------------+ + +**Return value:** + +None. + +**Remarks:** + +This is essentially a convenience function that sets ``status`` to ``302`` and the ``Location`` header to the provided URL. + +-------- + +.. _Response#status: + +``status`` +========== + +The HTTP status of the response (default = ``200``). + +-------- + +.. _Response#headers: + +``headers`` +=========== + +The response's HTTP header fields. Prefer :ref:`Response#get() `/:ref:`Response#set() ` over accessing this object directly. + +-------- + +.. _Response#body: + +``body`` +======== + +Key-value pairs of data to be submitted in the response body. + diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 000000000..d9aae7902 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,276 @@ +# -*- coding: utf-8 -*- +# +# oauth2-server documentation build configuration file, created by +# sphinx-quickstart on Thu Nov 17 16:47:05 2016. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os +from sphinx.errors import SphinxError + +sys.path.append(os.path.dirname(__file__)) +import npm_conf + +# Retrieve values from package.json used throughout the configuration. +config = npm_conf.get_config() + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +needs_sphinx = '1.3' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.intersphinx', 'sphinx.ext.ifconfig', 'sphinx.ext.todo'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = config['name'] +copyright = '{copyright_year}, {organization}'.format(**config) + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = config['short_version'] +# The full version, including alpha/beta/rc tags. +release = config['version'] + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +html_favicon = '_static/favicon.ico' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = '{name}doc'.format(**config) + + +# -- Options for LaTeX output -------------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', '{name}.tex'.format(**config), '{name} Documentation'.format(**config), + '{docs_author} \\textless{{}}{docs_author_email}\\textgreater{{}}'.format(**config), 'manual') +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', config['name'], '{name} Documentation'.format(**config), + ['{docs_author} <{docs_author_email}>'.format(**config)], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------------ + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', config['name'], '{name} Documentation'.format(**config), + '{docs_author} <{docs_author_email}>'.format(**config), + config['name'], 'OAuth2 server for Node.js.', 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = {'http://docs.python.org/': None} + +todo_include_todos = True + +on_rtd = os.environ.get('READTHEDOCS', None) == 'True' + +if not on_rtd: + try: + import sphinx_rtd_theme + except ImportError as err: + msg = [ + ' ' + str(err), + ' If you want to build docs locally please download the Read the Docs sphinx theme first:', + ' https://github.com/rtfd/sphinx_rtd_theme' + ] + raise SphinxError('\n'.join(msg)) + html_theme = 'sphinx_rtd_theme' + html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] + +html_theme_options = { + 'sticky_navigation': True +} + +highlight_language = 'js' + +def setup(app): + app.add_stylesheet('custom.css') + diff --git a/docs/docs/getting-started.rst b/docs/docs/getting-started.rst new file mode 100644 index 000000000..039f7c53b --- /dev/null +++ b/docs/docs/getting-started.rst @@ -0,0 +1,106 @@ +================= + Getting Started +================= + +.. _installation: + +Installation +============ + +oauth2-server_ is available via npm_. + +.. _oauth2-server: https://npmjs.org/package/oauth2-server +.. _npm: https://npmjs.org + +.. code-block:: sh + + $ npm install oauth2-server + +.. note:: The *oauth2-server* module is framework-agnostic but there are several officially supported wrappers available for popular HTTP server frameworks such as Express_ and Koa_. If you're using one of those frameworks it is strongly recommended to use the respective wrapper module instead of rolling your own. + +.. _Express: https://npmjs.org/package/express-oauth-server +.. _Koa: https://npmjs.org/package/koa-oauth-server + + +.. _features: + +Features +======== + +- Supports :ref:`authorization code `, :ref:`client credentials `, :ref:`refresh token ` and :ref:`password ` grant, as well as :ref:`extension grants `, with scopes. +- Can be used with *promises*, *Node-style callbacks*, *ES6 generators* and *async*/*await* (using Babel_). +- Fully :rfc:`6749` and :rfc:`6750` compliant. +- Implicitly supports any form of storage, e.g. *PostgreSQL*, *MySQL*, *MongoDB*, *Redis*, etc. +- Complete `test suite`_. + +.. _Babel: https://babeljs.io +.. _test suite: https://github.com/oauthjs/node-oauth2-server/tree/master/test + + +.. _quick-start: + +Quick Start +=========== + +:doc:`/api/oauth2-server` + +:: + + const OAuth2Server = require('oauth2-server'); + + const oauth = new OAuth2Server({ + model: require('./model') + }); + +:doc:`/api/request` and :doc:`/api/response` + +:: + + const Request = OAuth2Server.Request; + const Response = OAuth2Server.Response; + + let request = new Request({/*...*/}); + let response = new Response({/*...*/}); + +:ref:`OAuth2Server#authenticate() ` + +:: + + oauth.authenticate(request, response) + .then((token) => { + // The request was successfully authenticated. + }) + .catch((err) => { + // The request failed authentication. + }); + +:ref:`OAuth2Server#authorize() ` + +:: + + const AccessDeniedError = require('oauth2-server/lib/errors/access-denied-error'); + + oauth.authorize(request, response) + .then((code) => { + // The resource owner granted the access request. + }) + .catch((err) => { + if (err instanceof AccessDeniedError) { + // The resource owner denied the access request. + } else { + // Access was not granted due to some other error condition. + } + }); + +:ref:`OAuth2Server#token() ` + +:: + + oauth.token(request, response) + .then((token) => { + // The resource owner granted the access request. + }) + .catch((err) => { + // The request was invalid or not authorized. + }); + diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 000000000..94ea10030 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,87 @@ +=============== + oauth2-server +=============== + +oauth2-server_ is a complete, compliant and well tested module for implementing an OAuth2 server in Node.js_. The project is `hosted on GitHub`_ and the included test suite is automatically `run on Travis CI`_. + +.. _oauth2-server: https://npmjs.org/package/oauth2-server +.. _Node.js: https://nodejs.org +.. _hosted on GitHub: https://github.com/oauthjs/node-oauth2-server +.. _run on Travis CI: https://travis-ci.org/oauthjs/node-oauth2-server + +:ref:`installation` + + +Example Usage +============= + +:: + + const OAuth2Server = require('oauth2-server'); + const Request = OAuth2Server.Request; + const Response = OAuth2Server.Response; + + const oauth = new OAuth2Server({ + model: require('./model') + }); + + let request = new Request({ + method: 'GET', + query: {}, + headers: {Authorization: 'Bearer foobar'} + }); + + let response = new Response({ + headers: {} + }); + + oauth.authenticate(request, response) + .then((token) => { + // Request is authorized. + }) + .catch((err) => { + // Request is not authorized or an error occured. + }); + +See the :doc:`/model/spec` of what is required from the model passed to :doc:`/api/oauth2-server`. + + +.. toctree:: + :hidden: + + Home + +.. toctree:: + :maxdepth: 2 + :caption: User Documentation + :hidden: + + docs/getting-started + +.. toctree:: + :maxdepth: 2 + :caption: API + :includehidden: + :hidden: + + api/oauth2-server + api/request + api/response + api/errors/index + +.. toctree:: + :maxdepth: 3 + :caption: Model + :hidden: + + model/overview + model/spec + +.. toctree:: + :maxdepth: 2 + :caption: Miscellaneous + :hidden: + + misc/extension-grants + misc/migrating-v2-to-v3 + diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 000000000..7426caa00 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,190 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\oauth2-server.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\oauth2-server.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +:end diff --git a/docs/misc/extension-grants.rst b/docs/misc/extension-grants.rst new file mode 100644 index 000000000..5d454d906 --- /dev/null +++ b/docs/misc/extension-grants.rst @@ -0,0 +1,8 @@ +================== + Extension Grants +================== + +.. todo:: Describe how to implement extension grants. + +Extension grants are registered through :ref:`OAuth2Server#token() ` (``options.extendedGrantTypes``). + diff --git a/docs/misc/migrating-v2-to-v3.rst b/docs/misc/migrating-v2-to-v3.rst new file mode 100644 index 000000000..bd992dd8c --- /dev/null +++ b/docs/misc/migrating-v2-to-v3.rst @@ -0,0 +1,8 @@ +=========================== + Migrating from 2.x to 3.x +=========================== + +.. todo:: Copy `Migrating article`_ from GitHub Wiki. + +.. _Migrating article: https://github.com/oauthjs/node-oauth2-server/wiki/Migrating-from-2.x-to-3.x + diff --git a/docs/model/overview.rst b/docs/model/overview.rst new file mode 100644 index 000000000..cad28b0cb --- /dev/null +++ b/docs/model/overview.rst @@ -0,0 +1,129 @@ +================ + Model Overview +================ + +:doc:`/api/oauth2-server` requires a model object through which some aspects of storage, retrieval and custom validation are abstracted. + +-------- + +.. _GrantTypes: + +Grant Types +=========== + +:rfc:`6749` describes a number of grants for a client application to acquire an access token. + +The following grant types are supported by *oauth2-server*: + +-------- + +.. _AuthorizationCodeGrant: + +Authorization Code Grant +------------------------ + +See :rfc:`Section 4.1 of RFC 6749 <6749#section-4.1>`. + +An authorization code is a credential representing the resource owner's authorization (to access its protected resources) which is used by the client to obtain an access token. + +Model functions used by the authorization code grant: + +- :ref:`Model#generateAccessToken` +- :ref:`Model#generateRefreshToken` +- :ref:`Model#generateAuthorizationCode` +- :ref:`Model#getAuthorizationCode` +- :ref:`Model#getClient` +- :ref:`Model#saveToken` +- :ref:`Model#saveAuthorizationCode` +- :ref:`Model#revokeAuthorizationCode` +- :ref:`Model#validateScope` + +-------- + +.. _ClientCredentialsGrant: + +Client Credentials Grant +------------------------ + +See :rfc:`Section 4.4 of RFC 6749 <6749#section-4.4>`. + +The client can request an access token using only its client credentials (or other supported means of authentication) when requesting access to the protected resources under its control. + +.. note:: The client credentials grant type **must** only be used by confidential clients. + +Model functions used by the client credentials grant: + +- :ref:`Model#generateAccessToken` +- :ref:`Model#getClient` +- :ref:`Model#getUserFromClient` +- :ref:`Model#saveToken` +- :ref:`Model#validateScope` + +-------- + +.. _RefreshTokenGrant: + +Refresh Token Grant +------------------- + +See :rfc:`Section 6 of RFC 6749 <6749#section-6>`. + +If the authorization server issued a refresh token to the client, the client can request a refresh of their authorization token. + +Model functions used by the refresh token grant: + +- :ref:`Model#generateRefreshToken` +- :ref:`Model#getRefreshToken` +- :ref:`Model#getClient` +- :ref:`Model#saveToken` +- :ref:`Model#revokeToken` + +-------- + +.. _PasswordGrant: + +Password Grant +-------------- + +See :rfc:`Section 4.3 of RFC 6749 <6749#section-4.3>`. + +The password grant is suitable for clients capable of obtaining the resource owner's credentials (username and password, typically using an interactive form). + +Model functions used by the password grant: + +- :ref:`Model#generateAccessToken` +- :ref:`Model#generateRefreshToken` +- :ref:`Model#getClient` +- :ref:`Model#getUser` +- :ref:`Model#saveToken` +- :ref:`Model#validateScope` + +-------- + +.. _ExtensionGrants: + +Extension Grants +---------------- + +See :rfc:`Section 4.5 of RFC 6749 <6749#section-4.5>`. + +The authorization server may also implement custom grant types to issue access (and optionally refresh) tokens. + +See :doc:`/misc/extension-grants`. + +-------- + +.. _RequestAuthentication: + +Request Authentication +====================== + +.. todo:: Add a short description of request authentication. + +.. todo:: Reference :rfc:`6750`. + +Model functions used during request authentication: + +- :ref:`Model#getAccessToken` +- :ref:`Model#verifyScope` + diff --git a/docs/model/spec.rst b/docs/model/spec.rst new file mode 100644 index 000000000..21585f141 --- /dev/null +++ b/docs/model/spec.rst @@ -0,0 +1,981 @@ +===================== + Model Specification +===================== + +Each model function supports *promises*, *Node-style callbacks*, *ES6 generators* and *async*/*await* (using Babel_). Note that promise support implies support for returning plain values where asynchronism is not required. + +.. _Babel: https://babeljs.io + +:: + + const model = { + // We support returning promises. + getAccessToken: function() { + return new Promise('works!'); + }, + + // Or, calling a Node-style callback. + getAuthorizationCode: function(done) { + done(null, 'works!'); + }, + + // Or, using generators. + getClient: function*() { + yield somethingAsync(); + return 'works!'; + }, + + // Or, async/wait (using Babel). + getUser: async function() { + await somethingAsync(); + return 'works!'; + } + }; + + const OAuth2Server = require('oauth2-server'); + let oauth = new OAuth2Server({model: model}); + +Code examples on this page use *promises*. + +-------- + +.. _Model#generateAccessToken: + +``generateAccessToken(client, user, scope, [callback])`` +======================================================== + +Invoked to generate a new access token. + +This model function is **optional**. If not implemented, a default handler is used that generates access tokens consisting of 40 characters in the range of ``a..z0..9``. + +**Invoked during:** + +- ``authorization_code`` grant +- ``client_credentials`` grant +- ``refresh_token`` grant +- ``password`` grant + +**Arguments:** + ++------------+----------+---------------------------------------------------------------------+ +| Name | Type | Description | ++============+==========+=====================================================================+ +| client | Object | The client the access token is generated for. | ++------------+----------+---------------------------------------------------------------------+ +| user | Object | The user the access token is generated for. | ++------------+----------+---------------------------------------------------------------------+ +| scope | String | The scopes associated with the access token. Can be ``null``. | ++------------+----------+---------------------------------------------------------------------+ +| [callback] | Function | Node-style callback to be used instead of the returned ``Promise``. | ++------------+----------+---------------------------------------------------------------------+ + +**Return value:** + +A ``String`` to be used as access token. + +:rfc:`RFC 6749 Appendix A.12 <6749#appendix-A.12>` specifies that access tokens must consist of characters inside the range ``0x20..0x7E`` (i.e. only printable US-ASCII characters). + +**Remarks:** + +``client`` is the object previously obtained through :ref:`Model#getClient() `. + +``user`` is the user object previously obtained through :ref:`Model#getAuthorizationCode() ` (``code.user``; authorization code grant), :ref:`Model#getUserFromClient() ` (client credentials grant), :ref:`Model#getRefreshToken() ` (``token.user``; refresh token grant) or :ref:`Model#getUser() ` (password grant). + +-------- + +.. _Model#generateRefreshToken: + +``generateRefreshToken(client, user, scope, [callback])`` +========================================================= + +Invoked to generate a new refresh token. + +This model function is **optional**. If not implemented, a default handler is used that generates refresh tokens consisting of 40 characters in the range of ``a..z0..9``. + +**Invoked during:** + +- ``authorization_code`` grant +- ``refresh_token`` grant +- ``password`` grant + +**Arguments:** + ++------------+----------+---------------------------------------------------------------------+ +| Name | Type | Description | ++============+==========+=====================================================================+ +| client | Object | The client the refresh token is generated for. | ++------------+----------+---------------------------------------------------------------------+ +| user | Object | The user the refresh token is generated for. | ++------------+----------+---------------------------------------------------------------------+ +| scope | String | The scopes associated with the refresh token. Can be ``null``. | ++------------+----------+---------------------------------------------------------------------+ +| [callback] | Function | Node-style callback to be used instead of the returned ``Promise``. | ++------------+----------+---------------------------------------------------------------------+ + +**Return value:** + +A ``String`` to be used as refresh token. + +:rfc:`RFC 6749 Appendix A.17 <6749#appendix-A.17>` specifies that refresh tokens must consist of characters inside the range ``0x20..0x7E`` (i.e. only printable US-ASCII characters). + +**Remarks:** + +``client`` is the object previously obtained through :ref:`Model#getClient() `. + +``user`` is the user object previously obtained through :ref:`Model#getAuthorizationCode() ` (``code.user``; authorization code grant), :ref:`Model#getRefreshToken() ` (``token.user``; refresh token grant) or :ref:`Model#getUser() ` (password grant). + +-------- + +.. _Model#generateAuthorizationCode: + +``generateAuthorizationCode([callback])`` +========================================= + +Invoked to generate a new authorization code. + +This model function is **optional**. If not implemented, a default handler is used that generates authorization codes consisting of 40 characters in the range of ``a..z0..9``. + +**Invoked during:** + +- ``authorization_code`` grant + +**Arguments:** + ++------------+----------+---------------------------------------------------------------------+ +| Name | Type | Description | ++============+==========+=====================================================================+ +| [callback] | Function | Node-style callback to be used instead of the returned ``Promise``. | ++------------+----------+---------------------------------------------------------------------+ + +**Return value:** + +A ``String`` to be used as authorization code. + +:rfc:`RFC 6749 Appendix A.11 <6749#appendix-A.11>` specifies that authorization codes must consist of characters inside the range ``0x20..0x7E`` (i.e. only printable US-ASCII characters). + +-------- + +.. _Model#getAccessToken: + +``getAccessToken(accessToken, [callback])`` +=========================================== + +Invoked to retrieve an existing access token previously saved through :ref:`Model#saveToken() `. + +This model function is **required** if :ref:`OAuth2Server#authenticate() ` is used. + +**Invoked during:** + +- request authentication + +**Arguments:** + ++-------------+----------+---------------------------------------------------------------------+ +| Name | Type | Description | ++=============+==========+=====================================================================+ +| accessToken | String | The access token to retrieve. | ++-------------+----------+---------------------------------------------------------------------+ +| [callback] | Function | Node-style callback to be used instead of the returned ``Promise``. | ++-------------+----------+---------------------------------------------------------------------+ + +**Return value:** + +An ``Object`` representing the access token and associated data. + ++------------------------------+--------+--------------------------------------------------+ +| Name | Type | Description | ++==============================+========+==================================================+ +| token | Object | The return value. | ++------------------------------+--------+--------------------------------------------------+ +| token.accessToken | String | The access token passed to ``getAccessToken()``. | ++------------------------------+--------+--------------------------------------------------+ +| [token.accessTokenExpiresAt] | Date | The expiry time of the access token. | ++------------------------------+--------+--------------------------------------------------+ +| [token.scope] | String | The authorized scope of the access token. | ++------------------------------+--------+--------------------------------------------------+ +| token.client | Object | The client associated with the access token. | ++------------------------------+--------+--------------------------------------------------+ +| token.client.id | String | A unique string identifying the client. | ++------------------------------+--------+--------------------------------------------------+ +| token.user | Object | The user associated with the access token. | ++------------------------------+--------+--------------------------------------------------+ + +``token.client`` and ``token.user`` can carry additional properties that will be ignored by *oauth2-server*. + +**Remarks:** + +:: + + function getAccessToken(accessToken) { + // imaginary DB queries + db.queryAccessToken({access_token: accessToken}) + .then(function(token) { + return Promise.all([ + token, + db.queryClient({id: token.client_id}), + db.queryUser({id: token.user_id}) + ]); + }) + .spread(function(token, client, user) { + return { + accessToken: token.access_token, + accessTokenExpiresAt: token.expires_at, + scope: token.scope, + client: client, // with 'id' property + user: user + }; + }); + } + +-------- + +.. _Model#getRefreshToken: + +``getRefreshToken(refreshToken, [callback])`` +============================================= + +Invoked to retrieve an existing refresh token previously saved through :ref:`Model#saveToken() `. + +This model function is **required** if the ``refresh_token`` grant is used. + +**Invoked during:** + +- ``refresh_token`` grant + +**Arguments:** + ++--------------+----------+---------------------------------------------------------------------+ +| Name | Type | Description | ++==============+==========+=====================================================================+ +| refreshToken | String | The access token to retrieve. | ++--------------+----------+---------------------------------------------------------------------+ +| [callback] | Function | Node-style callback to be used instead of the returned ``Promise``. | ++--------------+----------+---------------------------------------------------------------------+ + +**Return value:** + +An ``Object`` representing the refresh token and associated data. + ++-------------------------------+--------+----------------------------------------------------+ +| Name | Type | Description | ++===============================+========+====================================================+ +| token | Object | The return value. | ++-------------------------------+--------+----------------------------------------------------+ +| token.refreshToken | String | The refresh token passed to ``getRefreshToken()``. | ++-------------------------------+--------+----------------------------------------------------+ +| [token.refreshTokenExpiresAt] | Date | The expiry time of the refresh token. | ++-------------------------------+--------+----------------------------------------------------+ +| [token.scope] | String | The authorized scope of the refresh token. | ++-------------------------------+--------+----------------------------------------------------+ +| token.client | Object | The client associated with the refresh token. | ++-------------------------------+--------+----------------------------------------------------+ +| token.client.id | String | A unique string identifying the client. | ++-------------------------------+--------+----------------------------------------------------+ +| token.user | Object | The user associated with the refresh token. | ++-------------------------------+--------+----------------------------------------------------+ + +``token.client`` and ``token.user`` can carry additional properties that will be ignored by *oauth2-server*. + +**Remarks:** + +:: + + function getRefreshToken(refreshToken) { + // imaginary DB queries + db.queryRefreshToken({refresh_token: refreshToken}) + .then(function(token) { + return Promise.all([ + token, + db.queryClient({id: token.client_id}), + db.queryUser({id: token.user_id}) + ]); + }) + .spread(function(token, client, user) { + return { + refreshToken: token.refresh_token, + refreshTokenExpiresAt: token.expires_at, + scope: token.scope, + client: client, // with 'id' property + user: user + }; + }); + } + +-------- + +.. _Model#getAuthorizationCode: + +``getAuthorizationCode(authorizationCode, [callback])`` +======================================================= + +Invoked to retrieve an existing authorization code previously saved through :ref:`Model#saveAuthorizationCode() `. + +This model function is **required** if the ``authorization_code`` grant is used. + +**Invoked during:** + +- ``authorization_code`` grant + +**Arguments:** + ++-------------------+----------+---------------------------------------------------------------------+ +| Name | Type | Description | ++===================+==========+=====================================================================+ +| authorizationCode | String | The authorization code to retrieve. | ++-------------------+----------+---------------------------------------------------------------------+ +| [callback] | Function | Node-style callback to be used instead of the returned ``Promise``. | ++-------------------+----------+---------------------------------------------------------------------+ + +**Return value:** + +An ``Object`` representing the authorization code and associated data. + ++--------------------+--------+--------------------------------------------------------------+ +| Name | Type | Description | ++====================+========+==============================================================+ +| code | Object | The return value. | ++--------------------+--------+--------------------------------------------------------------+ +| code.code | String | The authorization code passed to ``getAuthorizationCode()``. | ++--------------------+--------+--------------------------------------------------------------+ +| code.expiresAt | Date | The expiry time of the authorization code. | ++--------------------+--------+--------------------------------------------------------------+ +| [code.redirectUri] | String | The redirect URI of the authorization code. | ++--------------------+--------+--------------------------------------------------------------+ +| [code.scope] | String | The authorized scope of the authorization code. | ++--------------------+--------+--------------------------------------------------------------+ +| code.client | Object | The client associated with the authorization code. | ++--------------------+--------+--------------------------------------------------------------+ +| code.client.id | String | A unique string identifying the client. | ++--------------------+--------+--------------------------------------------------------------+ +| code.user | Object | The user associated with the authorization code. | ++--------------------+--------+--------------------------------------------------------------+ + +``code.client`` and ``code.user`` can carry additional properties that will be ignored by *oauth2-server*. + +**Remarks:** + +:: + + function getAuthorizationCode(authorizationCode) { + // imaginary DB queries + db.queryAuthorizationCode({authorization_code: authorizationCode}) + .then(function(code) { + return Promise.all([ + code, + db.queryClient({id: code.client_id}), + db.queryUser({id: code.user_id}) + ]); + }) + .spread(function(code, client, user) { + return { + code: code.authorization_code, + expiresAt: code.expires_at, + redirectUri: code.redirect_uri, + scope: code.scope, + client: client, // with 'id' property + user: user + }; + }); + } + +-------- + +.. _Model#getClient: + +``getClient(clientId, clientSecret, [callback])`` +================================================= + +Invoked to retrieve a client using a client id or a client id/client secret combination, depending on the grant type. + +This model function is **required** for all grant types. + +**Invoked during:** + +- ``authorization_code`` grant +- ``client_credentials`` grant +- ``refresh_token`` grant +- ``password`` grant + +**Arguments:** + ++--------------+----------+---------------------------------------------------------------------+ +| Name | Type | Description | ++==============+==========+=====================================================================+ +| clientId | String | The client id of the client to retrieve. | ++--------------+----------+---------------------------------------------------------------------+ +| clientSecret | String | The client secret of the client to retrieve. Can be ``null``. | ++--------------+----------+---------------------------------------------------------------------+ +| [callback] | Function | Node-style callback to be used instead of the returned ``Promise``. | ++--------------+----------+---------------------------------------------------------------------+ + +**Return value:** + +An ``Object`` representing the client and associated data, or a falsy value if no such client could be found. + ++-------------------------------+---------------+--------------------------------------------------------------------------------------+ +| Name | Type | Description | ++===============================+===============+======================================================================================+ +| client | Object | The return value. | ++-------------------------------+---------------+--------------------------------------------------------------------------------------+ +| client.id | String | A unique string identifying the client. | ++-------------------------------+---------------+--------------------------------------------------------------------------------------+ +| [client.redirectUris] | Array | Redirect URIs allowed for the client. Required for the ``authorization_code`` grant. | ++-------------------------------+---------------+--------------------------------------------------------------------------------------+ +| client.grants | Array | Grant types allowed for the client. | ++-------------------------------+---------------+--------------------------------------------------------------------------------------+ +| [client.accessTokenLifetime] | Number | Client-specific lifetime of generated access tokens in seconds. | ++-------------------------------+---------------+--------------------------------------------------------------------------------------+ +| [client.refreshTokenLifetime] | Number | Client-specific lifetime of generated refresh tokens in seconds. | ++-------------------------------+---------------+--------------------------------------------------------------------------------------+ + +The return value (``client``) can carry additional properties that will be ignored by *oauth2-server*. + +**Remarks:** + +:: + + function getClient(clientId, clientSecret) { + // imaginary DB query + let params = {client_id: clientId}; + if (clientSecret) { + params.client_secret = clientSecret; + } + db.queryClient(params) + .then(function(client) { + return { + id: client.id, + redirectUris: client.redirect_uris, + grants: client.grants + }; + }); + } + +-------- + +.. _Model#getUser: + +``getUser(username, password, [callback])`` +=========================================== + +Invoked to retrieve a user using a username/password combination. + +This model function is **required** if the ``password`` grant is used. + +**Invoked during:** + +- ``password`` grant + +**Arguments:** + ++------------+----------+---------------------------------------------------------------------+ +| Name | Type | Description | ++============+==========+=====================================================================+ +| username | String | The username of the user to retrieve. | ++------------+----------+---------------------------------------------------------------------+ +| password | String | The user's password. | ++------------+----------+---------------------------------------------------------------------+ +| [callback] | Function | Node-style callback to be used instead of the returned ``Promise``. | ++------------+----------+---------------------------------------------------------------------+ + +**Return value:** + +An ``Object`` representing the user, or a falsy value if no such user could be found. The user object is completely transparent to *oauth2-server* and is simply used as input to other model functions. + +**Remarks:** + +:: + + function getUser(username, password) { + // imaginary DB query + return db.queryUser({username: username, password: password}); + } + +-------- + +.. _Model#getUserFromClient: + +``getUserFromClient(client, [callback])`` +========================================= + +Invoked to retrieve the user associated with the specified client. + +This model function is **required** if the ``client_credentials`` grant is used. + +**Invoked during:** + +- ``client_credentials`` grant + +**Arguments:** + ++------------+----------+---------------------------------------------------------------------+ +| Name | Type | Description | ++============+==========+=====================================================================+ +| client | Object | The client to retrieve the associated user for. | ++------------+----------+---------------------------------------------------------------------+ +| client.id | String | A unique string identifying the client. | ++------------+----------+---------------------------------------------------------------------+ +| [callback] | Function | Node-style callback to be used instead of the returned ``Promise``. | ++------------+----------+---------------------------------------------------------------------+ + +**Return value:** + +An ``Object`` representing the user, or a falsy value if the client does not have an associated user. The user object is completely transparent to *oauth2-server* and is simply used as input to other model functions. + +**Remarks:** + +``client`` is the object previously obtained through :ref:`Model#getClient() `. + +:: + + function getUserFromClient(client) { + // imaginary DB query + return db.queryUser({id: client.user_id}); + } + +-------- + +.. _Model#saveToken: + +``saveToken(token, client, user, [callback])`` +============================================== + +Invoked to save an access token and optionally a refresh token, depending on the grant type. + +This model function is **required** for all grant types. + +**Invoked during:** + +- ``authorization_code`` grant +- ``client_credentials`` grant +- ``refresh_token`` grant +- ``password`` grant + +**Arguments:** + ++-------------------------------+----------+---------------------------------------------------------------------+ +| Name | Type | Description | ++===============================+==========+=====================================================================+ +| token | Object | The token(s) to be saved. | ++-------------------------------+----------+---------------------------------------------------------------------+ +| token.accessToken | String | The access token to be saved. | ++-------------------------------+----------+---------------------------------------------------------------------+ +| token.accessTokenExpiresAt | Date | The expiry time of the access token. | ++-------------------------------+----------+---------------------------------------------------------------------+ +| [token.refreshToken] | String | The refresh token to be saved. | ++-------------------------------+----------+---------------------------------------------------------------------+ +| [token.refreshTokenExpiresAt] | Date | The expiry time of the refresh token. | ++-------------------------------+----------+---------------------------------------------------------------------+ +| [token.scope] | String | The authorized scope of the token(s). | ++-------------------------------+----------+---------------------------------------------------------------------+ +| client | Object | The client associated with the token(s). | ++-------------------------------+----------+---------------------------------------------------------------------+ +| user | Object | The user associated with the token(s). | ++-------------------------------+----------+---------------------------------------------------------------------+ +| [callback] | Function | Node-style callback to be used instead of the returned ``Promise``. | ++-------------------------------+----------+---------------------------------------------------------------------+ + +**Return value:** + +An ``Object`` representing the token(s) and associated data. + ++-----------------------------+--------+----------------------------------------------+ +| Name | Type | Description | ++=============================+========+==============================================+ +| token | Object | The return value. | ++-----------------------------+--------+----------------------------------------------+ +| token.accessToken | String | The access token passed to ``saveToken()``. | ++-----------------------------+--------+----------------------------------------------+ +| token.accessTokenExpiresAt | Date | The expiry time of the access token. | ++-----------------------------+--------+----------------------------------------------+ +| token.refreshToken | String | The refresh token passed to ``saveToken()``. | ++-----------------------------+--------+----------------------------------------------+ +| token.refreshTokenExpiresAt | Date | The expiry time of the refresh token. | ++-----------------------------+--------+----------------------------------------------+ +| [token.scope] | String | The authorized scope of the access token. | ++-----------------------------+--------+----------------------------------------------+ +| token.client | Object | The client associated with the access token. | ++-----------------------------+--------+----------------------------------------------+ +| token.client.id | String | A unique string identifying the client. | ++-----------------------------+--------+----------------------------------------------+ +| token.user | Object | The user associated with the access token. | ++-----------------------------+--------+----------------------------------------------+ + +``token.client`` and ``token.user`` can carry additional properties that will be ignored by *oauth2-server*. + +If the ``allowExtendedTokenAttributes`` server option is enabled (see :ref:`OAuth2Server#token() `) any additional attributes set on the result are copied to the token response sent to the client. + +**Remarks:** + +:: + + function saveToken(token, client, user) { + // imaginary DB queries + let fns = [ + db.saveAccessToken({ + access_token: token.accessToken, + expires_at: token.accessTokenExpiresAt, + scope: token.scope, + client_id: client.id, + user_id: user.id + }), + db.saveRefreshToken({ + refresh_token: token.refreshToken, + expires_at: token.refreshTokenExpiresAt, + scope: token.scope, + client_id: client.id, + user_id: user.id + }) + ]; + return Promise.all(fns); + .spread(function(accessToken, refreshToken) { + return { + accessToken: accessToken.access_token, + accessTokenExpiresAt: accessToken.expires_at, + refreshToken: refreshToken.refresh_token, + refreshTokenExpiresAt: refreshToken.expires_at, + scope: accessToken.scope, + client: {id: accessToken.client_id}, + user: {id: accessToken.user_id} + }; + }); + } + +-------- + +.. _Model#saveAuthorizationCode: + +``saveAuthorizationCode(code, client, user, [callback])`` +========================================================= + +Invoked to save an authorization code. + +This model function is **required** if the ``authorization_code`` grant is used. + +**Invoked during:** + +- ``authorization_code`` grant + +**Arguments:** + ++------------------------+----------+---------------------------------------------------------------------+ +| Name | Type | Description | ++========================+==========+=====================================================================+ +| code | Object | The code to be saved. | ++------------------------+----------+---------------------------------------------------------------------+ +| code.authorizationCode | String | The authorization code to be saved. | ++------------------------+----------+---------------------------------------------------------------------+ +| code.expiresAt | Date | The expiry time of the authorization code. | ++------------------------+----------+---------------------------------------------------------------------+ +| code.redirectUri | String | The redirect URI associated with the authorization code. | ++------------------------+----------+---------------------------------------------------------------------+ +| [code.scope] | String | The authorized scope of the authorization code. | ++------------------------+----------+---------------------------------------------------------------------+ +| client | Object | The client associated with the authorization code. | ++------------------------+----------+---------------------------------------------------------------------+ +| user | Object | The user associated with the authorization code. | ++------------------------+----------+---------------------------------------------------------------------+ +| [callback] | Function | Node-style callback to be used instead of the returned ``Promise``. | ++------------------------+----------+---------------------------------------------------------------------+ + +.. todo:: Is ``code.scope`` really optional? + +**Return value:** + +An ``Object`` representing the authorization code and associated data. + ++------------------------+--------+---------------------------------------------------------------+ +| Name | Type | Description | ++========================+========+===============================================================+ +| code | Object | The return value. | ++------------------------+--------+---------------------------------------------------------------+ +| code.authorizationCode | String | The authorization code passed to ``saveAuthorizationCode()``. | ++------------------------+--------+---------------------------------------------------------------+ +| code.expiresAt | Date | The expiry time of the authorization code. | ++------------------------+--------+---------------------------------------------------------------+ +| code.redirectUri | String | The redirect URI associated with the authorization code. | ++------------------------+--------+---------------------------------------------------------------+ +| [code.scope] | String | The authorized scope of the authorization code. | ++------------------------+--------+---------------------------------------------------------------+ +| code.client | Object | The client associated with the authorization code. | ++------------------------+--------+---------------------------------------------------------------+ +| code.client.id | String | A unique string identifying the client. | ++------------------------+--------+---------------------------------------------------------------+ +| code.user | Object | The user associated with the authorization code. | ++------------------------+--------+---------------------------------------------------------------+ + +``code.client`` and ``code.user`` can carry additional properties that will be ignored by *oauth2-server*. + +**Remarks:** + +:: + + function saveAuthorizationCode(code, client, user) { + // imaginary DB queries + let authCode = { + authorization_code: code.authorizationCode, + expires_at: code.expiresAt, + redirect_uri: code.redirectUri, + scope: code.scope, + client_id: client.id, + user_id: user.id + }; + return db.saveAuthorizationCode(authCode) + .then(function(authorizationCode) { + return { + authorizationCode: authorizationCode.authorization_code, + expiresAt: authorizationCode.expires_at, + redirectUri: authorizationCode.redirect_uri, + scope: authorizationCode.scope, + client: {id: authorizationCode.client_id}, + user: {id: authorizationCode.user_id} + }; + }); + } + +-------- + +.. _Model#revokeToken: + +``revokeToken(token, [callback])`` +================================== + +Invoked to revoke a refresh token. + +This model function is **required** if the ``refresh_token`` grant is used. + +**Invoked during:** + +- ``refresh_token`` grant + +**Arguments:** + ++-------------------------------+----------+---------------------------------------------------------------------+ +| Name | Type | Description | ++===============================+==========+=====================================================================+ +| token | Object | The token to be revoked. | ++-------------------------------+----------+---------------------------------------------------------------------+ +| token.refreshToken | String | The refresh token. | ++-------------------------------+----------+---------------------------------------------------------------------+ +| [token.refreshTokenExpiresAt] | Date | The expiry time of the refresh token. | ++-------------------------------+----------+---------------------------------------------------------------------+ +| [token.scope] | String | The authorized scope of the refresh token. | ++-------------------------------+----------+---------------------------------------------------------------------+ +| token.client | Object | The client associated with the refresh token. | ++-------------------------------+----------+---------------------------------------------------------------------+ +| token.client.id | String | A unique string identifying the client. | ++-------------------------------+----------+---------------------------------------------------------------------+ +| token.user | Object | The user associated with the refresh token. | ++-------------------------------+----------+---------------------------------------------------------------------+ +| [callback] | Function | Node-style callback to be used instead of the returned ``Promise``. | ++-------------------------------+----------+---------------------------------------------------------------------+ + +**Return value:** + +Return ``true`` if the revocation was successful or ``false`` if the refresh token could not be found. + +**Remarks:** + +``token`` is the refresh token object previously obtained through :ref:`Model#getRefreshToken() `. + +:: + + function revokeToken(token) { + // imaginary DB queries + return db.deleteRefreshToken({refresh_token: token.refreshToken}) + .then(function(refreshToken) { + return !!refreshToken; + }); + } + +-------- + +.. _Model#revokeAuthorizationCode: + +``revokeAuthorizationCode(code, [callback])`` +============================================= + +Invoked to revoke an authorization code. + +This model function is **required** if the ``authorization_code`` grant is used. + +**Invoked during:** + +- ``authorization_code`` grant + +**Arguments:** + ++--------------------+----------+---------------------------------------------------------------------+ +| Name | Type | Description | ++====================+==========+=====================================================================+ +| code | Object | The return value. | ++--------------------+----------+---------------------------------------------------------------------+ +| code.code | String | The authorization code. | ++--------------------+----------+---------------------------------------------------------------------+ +| code.expiresAt | Date | The expiry time of the authorization code. | ++--------------------+----------+---------------------------------------------------------------------+ +| [code.redirectUri] | String | The redirect URI of the authorization code. | ++--------------------+----------+---------------------------------------------------------------------+ +| [code.scope] | String | The authorized scope of the authorization code. | ++--------------------+----------+---------------------------------------------------------------------+ +| code.client | Object | The client associated with the authorization code. | ++--------------------+----------+---------------------------------------------------------------------+ +| code.client.id | String | A unique string identifying the client. | ++--------------------+----------+---------------------------------------------------------------------+ +| code.user | Object | The user associated with the authorization code. | ++--------------------+----------+---------------------------------------------------------------------+ +| [callback] | Function | Node-style callback to be used instead of the returned ``Promise``. | ++--------------------+----------+---------------------------------------------------------------------+ + +**Return value:** + +Return ``true`` if the revocation was successful or ``false`` if the authorization code could not be found. + +**Remarks:** + +``code`` is the authorization code object previously obtained through :ref:`Model#getAuthorizationCode() `. + +:: + + function revokeAuthorizationCode(code) { + // imaginary DB queries + return db.deleteAuthorizationCode({authorization_code: code.authorizationCode}) + .then(function(authorizationCode) { + return !!authorizationCode; + }); + } + +-------- + +.. _Model#validateScope: + +``validateScope(user, client, scope, [callback])`` +================================================== + +Invoked to check if the requested ``scope`` is valid for a particular ``client``/``user`` combination. + +This model function is **optional**. If not implemented, any scope is accepted. + +**Invoked during:** + +- ``authorization_code`` grant +- ``client_credentials`` grant +- ``password`` grant + +**Arguments:** + ++------------+----------+---------------------------------------------------------------------+ +| Name | Type | Description | ++============+==========+=====================================================================+ +| user | Object | The associated user. | ++------------+----------+---------------------------------------------------------------------+ +| client | Object | The associated client. | ++------------+----------+---------------------------------------------------------------------+ +| client.id | Object | A unique string identifying the client. | ++------------+----------+---------------------------------------------------------------------+ +| scope | String | The scopes to validate. | ++------------+----------+---------------------------------------------------------------------+ +| [callback] | Function | Node-style callback to be used instead of the returned ``Promise``. | ++------------+----------+---------------------------------------------------------------------+ + +**Return value:** + +Validated scopes to be used or a falsy value to reject the requested scopes. + +**Remarks:** + +``user`` is the user object previously obtained through :ref:`Model#getAuthorizationCode() ` (``code.user``; authorization code grant), :ref:`Model#getUserFromClient() ` (client credentials grant) or :ref:`Model#getUser() ` (password grant). + +``client`` is the object previously obtained through :ref:`Model#getClient ` (all grants). + +You can decide yourself whether you want to reject or accept partially valid scopes by simply filtering out invalid scopes and returning only the valid ones. + +To reject invalid or only partially valid scopes: + +:: + + // list of valid scopes + const VALID_SCOPES = ['read', 'write']; + + function validateScope(user, client, scope) { + if (!scope.split(' ').every(s => VALID_SCOPES.indexOf(s) >= 0)) { + return false; + } + return scope; + } + +To accept partially valid scopes: + +:: + + // list of valid scopes + const VALID_SCOPES = ['read', 'write']; + + function validateScope(user, client, scope) { + return scope + .split(' ') + .filter(s => VALID_SCOPES.indexOf(s) >= 0) + .join(' '); + } + +Note that the example above will still reject completely invalid scopes, since ``validateScope`` returns an empty string if all scopes are filtered out. + +-------- + +.. _Model#verifyScope: + +``verifyScope(accessToken, scope, [callback])`` +=============================================== + +Invoked during request authentication to check if the provided access token was authorized the requested scopes. + +This model function is **required** if scopes are used with :ref:`OAuth2Server#authenticate() `. + +**Invoked during:** + +- request authentication + +**Arguments:** + ++------------------------------+----------+---------------------------------------------------------------------+ +| Name | Type | Description | ++==============================+==========+=====================================================================+ +| token | Object | The access token to test against | ++------------------------------+----------+---------------------------------------------------------------------+ +| token.accessToken | String | The access token. | ++------------------------------+----------+---------------------------------------------------------------------+ +| [token.accessTokenExpiresAt] | Date | The expiry time of the access token. | ++------------------------------+----------+---------------------------------------------------------------------+ +| [token.scope] | String | The authorized scope of the access token. | ++------------------------------+----------+---------------------------------------------------------------------+ +| token.client | Object | The client associated with the access token. | ++------------------------------+----------+---------------------------------------------------------------------+ +| token.client.id | String | A unique string identifying the client. | ++------------------------------+----------+---------------------------------------------------------------------+ +| token.user | Object | The user associated with the access token. | ++------------------------------+----------+---------------------------------------------------------------------+ +| scope | String | The required scopes. | ++------------------------------+----------+---------------------------------------------------------------------+ +| [callback] | Function | Node-style callback to be used instead of the returned ``Promise``. | ++------------------------------+----------+---------------------------------------------------------------------+ + +**Return value:** + +Returns ``true`` if the access token passes, ``false`` otherwise. + +**Remarks:** + +``token`` is the access token object previously obtained through :ref:`Model#getAccessToken() `. + +``scope`` is the required scope as given to :ref:`OAuth2Server#authenticate() ` as ``options.scope``. + +:: + + function verifyScope(token, scope) { + if (!token.scope) { + return false; + } + let requestedScopes = scope.split(' '); + let authorizedScopes = token.scope.split(' '); + return requestedScopes.every(s => authorizedScopes.indexOf(s) >= 0); + } + diff --git a/docs/npm_conf.py b/docs/npm_conf.py new file mode 100644 index 000000000..f86915f51 --- /dev/null +++ b/docs/npm_conf.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +""" Helper module for sphinx' conf.py. + + Useful when building documentation for npm packages. +""" + +import json +import re +from datetime import datetime + +def load_package_json(path): + """ Loads package.json from 'path'. + """ + with open(path) as f: + return json.load(f) + +def get_short_version(version): + """ Extracts the short version ("x.y.z") from 'version'. + """ + match = re.match('^(\d+(?:\.\d+){2})', version) + if not match: + raise Error('invalid version') + return match.group() + +def get_copyright_year(base_year): + """ Returns the year(s) to be shown in the copyright notice. + + If base_year is the current year: + 'nnnn' where nnnn is 'base_year' + If the current year is larger than base_year: + 'nnnn-mmmm' where nnnn is 'base_year' and mmmm is the current year + """ + this_year = datetime.now().year + fmt = '{base_year}-{this_year}' if this_year > base_year else '{this_year}' + return fmt.format(base_year=base_year, this_year=this_year) + +def get_config(): + package = load_package_json('../package.json') + return { + 'name': package['name'], + 'version': package['version'], + 'short_version': get_short_version(package['version']), + 'organization': 'oauthjs', + 'copyright_year': get_copyright_year(2016), + # TODO: Get authors from package. + 'docs_author': 'Max Truxa', + 'docs_author_email': 'dev@maxtruxa.com' + } + From 0f7d0bf9f39c804de48d012be56ebd19bdbd2860 Mon Sep 17 00:00:00 2001 From: Max Truxa Date: Fri, 10 Mar 2017 11:00:01 +0100 Subject: [PATCH 02/11] Fix broken table formatting --- docs/model/spec.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/model/spec.rst b/docs/model/spec.rst index 21585f141..5286663f3 100644 --- a/docs/model/spec.rst +++ b/docs/model/spec.rst @@ -557,15 +557,15 @@ This model function is **required** for all grant types. +===============================+==========+=====================================================================+ | token | Object | The token(s) to be saved. | +-------------------------------+----------+---------------------------------------------------------------------+ -| token.accessToken | String | The access token to be saved. | +| token.accessToken | String | The access token to be saved. | +-------------------------------+----------+---------------------------------------------------------------------+ -| token.accessTokenExpiresAt | Date | The expiry time of the access token. | +| token.accessTokenExpiresAt | Date | The expiry time of the access token. | +-------------------------------+----------+---------------------------------------------------------------------+ -| [token.refreshToken] | String | The refresh token to be saved. | +| [token.refreshToken] | String | The refresh token to be saved. | +-------------------------------+----------+---------------------------------------------------------------------+ -| [token.refreshTokenExpiresAt] | Date | The expiry time of the refresh token. | +| [token.refreshTokenExpiresAt] | Date | The expiry time of the refresh token. | +-------------------------------+----------+---------------------------------------------------------------------+ -| [token.scope] | String | The authorized scope of the token(s). | +| [token.scope] | String | The authorized scope of the token(s). | +-------------------------------+----------+---------------------------------------------------------------------+ | client | Object | The client associated with the token(s). | +-------------------------------+----------+---------------------------------------------------------------------+ From 0a21723e8d6be14de47cef5f3530172227f68e43 Mon Sep 17 00:00:00 2001 From: Max Truxa Date: Mon, 13 Mar 2017 08:42:35 +0100 Subject: [PATCH 03/11] Unify code example in index.rst --- docs/index.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 94ea10030..ccba6532f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -37,10 +37,10 @@ Example Usage oauth.authenticate(request, response) .then((token) => { - // Request is authorized. + // The request was successfully authenticated. }) .catch((err) => { - // Request is not authorized or an error occured. + // The request failed authentication. }); See the :doc:`/model/spec` of what is required from the model passed to :doc:`/api/oauth2-server`. From d477b315fad9ebb3b72915cc14d32421efd6c8a1 Mon Sep 17 00:00:00 2001 From: Max Truxa Date: Thu, 16 Mar 2017 18:07:42 +0100 Subject: [PATCH 04/11] docs: Add InsufficientScopeError --- docs/api/errors/index.rst | 1 + docs/api/errors/insufficient-scope-error.rst | 82 ++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 docs/api/errors/insufficient-scope-error.rst diff --git a/docs/api/errors/index.rst b/docs/api/errors/index.rst index f2aa79882..195d735d1 100644 --- a/docs/api/errors/index.rst +++ b/docs/api/errors/index.rst @@ -23,6 +23,7 @@ Noteable error types: server-error invalid-argument-error access-denied-error + insufficient-scope-error invalid-client-error invalid-grant-error invalid-request-error diff --git a/docs/api/errors/insufficient-scope-error.rst b/docs/api/errors/insufficient-scope-error.rst new file mode 100644 index 000000000..be3539de0 --- /dev/null +++ b/docs/api/errors/insufficient-scope-error.rst @@ -0,0 +1,82 @@ +======================== + InsufficientScopeError +======================== + +The request requires higher privileges than provided by the access token. See :rfc:`Section 3.1 of RFC 6750 <6750#section-3.1>`. + +:: + + const InsufficientScopeError = require('oauth2-server/lib/errors/insufficient-scope-error'); + +-------- + +.. _InsufficientScopeError#constructor: + +``new InsufficientScopeError(message, properties)`` +=================================================== + +Instantiates an ``InsufficientScopeError``. + +**Arguments:** + ++----------------------------------------+--------------+-------------------------------------------------------------+ +| Name | Type | Description | ++========================================+==============+=============================================================+ +| [message=undefined] | String|Error | See :ref:`OAuthError#constructor`. | ++----------------------------------------+--------------+-------------------------------------------------------------+ +| [properties={}] | Object | See :ref:`OAuthError#constructor`. | ++----------------------------------------+--------------+-------------------------------------------------------------+ +| [properties.code=403] | Object | See :ref:`OAuthError#constructor`. | ++----------------------------------------+--------------+-------------------------------------------------------------+ +| [properties.name='insufficient_scope'] | String | The error name used in responses generated from this error. | ++----------------------------------------+--------------+-------------------------------------------------------------+ + +**Return value:** + +A new instance of ``InsufficientScopeError``. + +**Remarks:** + +:: + + const err = new InsufficientScopeError(); + // err.message === 'Forbidden' + // err.code === 403 + // err.name === 'insufficient_scope' + +-------- + +.. _InsufficientScopeError#message: + +``message`` +=========== + +See :ref:`OAuthError#message `. + +-------- + +.. _InsufficientScopeError#code: + +``code`` +======== + +Typically ``403``. See :ref:`OAuthError#code `. + +-------- + +.. _InsufficientScopeError#inner: + +``inner`` +========= + +See :ref:`OAuthError#inner `. + +-------- + +.. _InsufficientScopeError#name: + +``name`` +======== + +Typically ``'insufficient_scope'``. See :ref:`OAuthError#name `. + From 911e9b73ec5fad4fc446ed728afbcc3f509b3e1c Mon Sep 17 00:00:00 2001 From: Max Truxa Date: Thu, 16 Mar 2017 18:12:01 +0100 Subject: [PATCH 05/11] docs: Rename requiresClientAuthentication to requireClientAuthentication --- docs/api/oauth2-server.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api/oauth2-server.rst b/docs/api/oauth2-server.rst index b2dd784fd..cbc57501d 100644 --- a/docs/api/oauth2-server.rst +++ b/docs/api/oauth2-server.rst @@ -233,7 +233,7 @@ Retrieves a new token for an authorized token request. +----------------------------------------------+-----------------+-------------------------------------------------------------------------------------------+ | [options.allowExtendedTokenAttributes=false] | Boolean | Allow extended attributes to be set on the returned token (see remarks section). | +----------------------------------------------+-----------------+-------------------------------------------------------------------------------------------+ -| [options.requiresClientAuthentication={}] | Object | Require a client secret (see remarks section). Defaults to ``true`` for all grant types. | +| [options.requireClientAuthentication={}] | Object | Require a client secret (see remarks section). Defaults to ``true`` for all grant types. | +----------------------------------------------+-----------------+-------------------------------------------------------------------------------------------+ | [options.alwaysIssueNewRefreshToken=true] | Boolean | Always revoke the used refresh token and issue a new one for the ``refresh_token`` grant. | +----------------------------------------------+-----------------+-------------------------------------------------------------------------------------------+ @@ -258,7 +258,7 @@ The returned ``Promise`` **must** be ignored if ``callback`` is used. If ``options.allowExtendedTokenAttributes`` is ``true`` any additional properties set on the object returned from :ref:`Model#saveToken() ` are copied to the token response sent to the client. -.. todo:: ``options.requiresClientAuthentication`` +.. todo:: ``options.requireClientAuthentication`` ``options.extendedGrantTypes`` is an object mapping extension grant URIs to handler types, for example: From 32875700aaf04200af538e8570013c82635a6405 Mon Sep 17 00:00:00 2001 From: Max Truxa Date: Thu, 16 Mar 2017 18:44:13 +0100 Subject: [PATCH 06/11] docs: Fix styling of literals in
tags --- docs/_static/custom.css | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/_static/custom.css b/docs/_static/custom.css index 4444839ad..fc9972102 100644 --- a/docs/_static/custom.css +++ b/docs/_static/custom.css @@ -40,12 +40,17 @@ font-size: 85% !important; } -/* reduce left-/right-padding of literals from 5px to 3px */ +/* reduce left/right padding of literals from 5px to 3px */ .rst-content code.literal { padding-left: 3px !important; padding-right: 3px !important; } +/* reset font-size of literals inside the term definition (
) in description lists */ +.rst-content dl dt code.literal { + font-size: 100% !important; +} + /* external links generated by the :rfc: role are surrounded by * tags which doesn't look good in floating text */ .rst-content a.rfc strong { From 48c6fef3110c8b2ef3ebfe63952eb1cd8592c31e Mon Sep 17 00:00:00 2001 From: Max Truxa Date: Mon, 20 Mar 2017 10:06:15 +0100 Subject: [PATCH 07/11] docs: Add UnsupportedResponseTypeError --- docs/api/errors/index.rst | 1 + .../unsupported-response-type-error.rst | 82 +++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 docs/api/errors/unsupported-response-type-error.rst diff --git a/docs/api/errors/index.rst b/docs/api/errors/index.rst index 195d735d1..382cbfa08 100644 --- a/docs/api/errors/index.rst +++ b/docs/api/errors/index.rst @@ -32,4 +32,5 @@ Noteable error types: unauthorized-client-error unauthorized-request-error unsupported-grant-type-error + unsupported-response-type-error diff --git a/docs/api/errors/unsupported-response-type-error.rst b/docs/api/errors/unsupported-response-type-error.rst new file mode 100644 index 000000000..28974eba1 --- /dev/null +++ b/docs/api/errors/unsupported-response-type-error.rst @@ -0,0 +1,82 @@ +============================== + UnsupportedResponseTypeError +============================== + +The authorization server does not supported obtaining an authorization code using this method. See :rfc:`Section 4.1.2.1 of RFC 6749 <6749#section-4.1.2.1>`. + +:: + + const UnsupportedResponseTypeError = require('oauth2-server/lib/errors/unsupported-response-type-error'); + +-------- + +.. _UnsupportedResponseTypeError#constructor: + +``new UnsupportedResponseTypeError(message, properties)`` +========================================================= + +Instantiates an ``UnsupportedResponseTypeError``. + +**Arguments:** + ++-----------------------------------------------+--------------+-------------------------------------------------------------+ +| Name | Type | Description | ++===============================================+==============+=============================================================+ +| [message=undefined] | String|Error | See :ref:`OAuthError#constructor`. | ++-----------------------------------------------+--------------+-------------------------------------------------------------+ +| [properties={}] | Object | See :ref:`OAuthError#constructor`. | ++-----------------------------------------------+--------------+-------------------------------------------------------------+ +| [properties.code=400] | Object | See :ref:`OAuthError#constructor`. | ++-----------------------------------------------+--------------+-------------------------------------------------------------+ +| [properties.name='unsupported_response_type'] | String | The error name used in responses generated from this error. | ++-----------------------------------------------+--------------+-------------------------------------------------------------+ + +**Return value:** + +A new instance of ``UnsupportedResponseTypeError``. + +**Remarks:** + +:: + + const err = new UnsupportedResponseTypeError(); + // err.message === 'Bad Request' + // err.code === 400 + // err.name === 'unsupported_response_type' + +-------- + +.. _UnsupportedResponseTypeError#message: + +``message`` +=========== + +See :ref:`OAuthError#message `. + +-------- + +.. _UnsupportedResponseTypeError#code: + +``code`` +======== + +Typically ``400``. See :ref:`OAuthError#code `. + +-------- + +.. _UnsupportedResponseTypeError#inner: + +``inner`` +========= + +See :ref:`OAuthError#inner `. + +-------- + +.. _UnsupportedResponseTypeError#name: + +``name`` +======== + +Typically ``'unsupported_response_type'``. See :ref:`OAuthError#name `. + From 6bce5fc2890ceefbae5f2c97bdd550427333b4e9 Mon Sep 17 00:00:00 2001 From: Max Truxa Date: Thu, 13 Apr 2017 18:56:11 +0200 Subject: [PATCH 08/11] docs: Describe adapters --- docs/docs/adapters.rst | 36 +++++++++++++++++++++++++++++++++++ docs/docs/getting-started.rst | 2 +- docs/index.rst | 1 + 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 docs/docs/adapters.rst diff --git a/docs/docs/adapters.rst b/docs/docs/adapters.rst new file mode 100644 index 000000000..c302d34e5 --- /dev/null +++ b/docs/docs/adapters.rst @@ -0,0 +1,36 @@ +========== + Adapters +========== + +The *oauth2-server* module is typically not used directly but through one of the available adapters, converting the interface to a suitable one for the HTTP server framework in use. + +.. framework-agnostic but there are several officially supported adapters available for popular HTTP server frameworks such as Express_ and Koa_. + +- express-oauth-server_ for Express_ +- koa-oauth-server_ for Koa_ + +.. _express-oauth-server: https://npmjs.org/package/express-oauth-server +.. _Express: https://npmjs.org/package/express +.. _koa-oauth-server: https://npmjs.org/package/koa-oauth-server +.. _Koa: https://npmjs.org/package/koa + + +Writing Adapters +================ + +Adapters typically do the following: + +- Inherit from :doc:`OAuth2Server `. + +- Override :ref:`authenticate() `, :ref:`authorize() ` and :ref:`token() `. + + Each of these functions should: + + - Create :doc:`Request ` and :doc:`Response ` objects from their framework-specific counterparts. + + - Call the original function. + + - Copy all fields from the :doc:`Response ` back to the framework-specific request object and send it. + +Adapters should preserve functionality provided by *oauth2-server* but are free to add additional features that make sense for the respective HTTP server framework. + diff --git a/docs/docs/getting-started.rst b/docs/docs/getting-started.rst index 039f7c53b..ff2c1156b 100644 --- a/docs/docs/getting-started.rst +++ b/docs/docs/getting-started.rst @@ -16,7 +16,7 @@ oauth2-server_ is available via npm_. $ npm install oauth2-server -.. note:: The *oauth2-server* module is framework-agnostic but there are several officially supported wrappers available for popular HTTP server frameworks such as Express_ and Koa_. If you're using one of those frameworks it is strongly recommended to use the respective wrapper module instead of rolling your own. +.. note:: The *oauth2-server* module is framework-agnostic but there are several officially supported adapters available for popular HTTP server frameworks such as Express_ and Koa_. If you're using one of those frameworks it is strongly recommended to use the respective adapter module instead of rolling your own. .. _Express: https://npmjs.org/package/express-oauth-server .. _Koa: https://npmjs.org/package/koa-oauth-server diff --git a/docs/index.rst b/docs/index.rst index ccba6532f..4a7c3415f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -57,6 +57,7 @@ See the :doc:`/model/spec` of what is required from the model passed to :doc:`/a :hidden: docs/getting-started + docs/adapters .. toctree:: :maxdepth: 2 From 9354626992bba072e2a497ec78cbe3e2bb37ffdc Mon Sep 17 00:00:00 2001 From: Max Truxa Date: Thu, 13 Apr 2017 19:11:23 +0200 Subject: [PATCH 09/11] readme: Add Slack badge --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 4a22598ec..2f56c8376 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![npm Downloads][downloads-image]][downloads-url] [![Test Status][travis-image]][travis-url] [![MIT Licensed][license-image]][license-url] +[![oauthjs Slack][slack-image]][slack-url] Complete, compliant and well tested module for implementing an OAuth2 server in [Node.js](https://nodejs.org). @@ -69,4 +70,6 @@ npm test [travis-url]: https://travis-ci.org/oauthjs/node-oauth2-server [license-image]: https://img.shields.io/badge/license-MIT-blue.svg [license-url]: https://raw.githubusercontent.com/oauthjs/node-oauth2-server/master/LICENSE +[slack-image]: https://img.shields.io/badge/slack-join-E01563.svg +[slack-url]: https://oauthjs.slack.com From e1e11cb3ab3534a8698daa7eda71facd92695e20 Mon Sep 17 00:00:00 2001 From: Max Truxa Date: Fri, 21 Apr 2017 12:15:56 +0200 Subject: [PATCH 10/11] docs: Describe the requireClientAuthentication server option --- docs/api/oauth2-server.rst | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/api/oauth2-server.rst b/docs/api/oauth2-server.rst index cbc57501d..48acf538a 100644 --- a/docs/api/oauth2-server.rst +++ b/docs/api/oauth2-server.rst @@ -258,7 +258,15 @@ The returned ``Promise`` **must** be ignored if ``callback`` is used. If ``options.allowExtendedTokenAttributes`` is ``true`` any additional properties set on the object returned from :ref:`Model#saveToken() ` are copied to the token response sent to the client. -.. todo:: ``options.requireClientAuthentication`` +By default all grant types require the client to send it's ``client_secret`` with the token request. ``options.requireClientAuthentication`` can be used to disable this check for selected grants. If used, this server option must be an object containing properties set to ``true`` or ``false``. Possible keys for the object include all supported values for the token request's ``grant_type`` field (``authorization_code``, ``client_credentials``, ``password`` and ``refresh_token``). Grants that are not specified default to ``true`` which enables verification of the ``client_secret``. + +:: + + let options = { + // ... + // Allow token requests using the password grant to not include a client_secret. + requireClientAuthentication: {password: false} + }; ``options.extendedGrantTypes`` is an object mapping extension grant URIs to handler types, for example: From 89ef3b553d73d27ce433705356d0cb4956e3649c Mon Sep 17 00:00:00 2001 From: Max Truxa Date: Fri, 21 Apr 2017 12:17:18 +0200 Subject: [PATCH 11/11] docs: Add description of request authentication --- docs/model/overview.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/model/overview.rst b/docs/model/overview.rst index cad28b0cb..5e345abd0 100644 --- a/docs/model/overview.rst +++ b/docs/model/overview.rst @@ -118,9 +118,9 @@ See :doc:`/misc/extension-grants`. Request Authentication ====================== -.. todo:: Add a short description of request authentication. +See :rfc:`Section 2 of RFC 6750 <6750#section-2>`. -.. todo:: Reference :rfc:`6750`. +The authorization server authenticates requests sent to the resource server by verifying the included bearer token. Model functions used during request authentication: