Since we allow projects to implement their own model (while providing ours
36 | as drop-in) we still need to validate, whether they implement the model
37 | correctly.
38 |
We duck-type check if the model implements the most important functions.
39 | Uses the following values to check:
Wrapped WebApp with express-style get/post and default use routes.
61 |
62 |
63 |
64 |
65 |
66 | ## OAuthMeteorModel
67 | Implements the OAuth2Server model with Meteor-Mongo bindings.
68 |
69 | **Kind**: global class
70 |
71 | * [OAuthMeteorModel](#OAuthMeteorModel)
72 | * [.log(...args)](#OAuthMeteorModel+log)
73 | * [.getAccessToken()](#OAuthMeteorModel+getAccessToken)
74 | * [.createClient(title, homepage, description, privacyLink, redirectUris, grants, clientId, secret)](#OAuthMeteorModel+createClient) ⇒ Promise.<Object>
75 | * [.getClient()](#OAuthMeteorModel+getClient)
76 | * [.saveToken()](#OAuthMeteorModel+saveToken)
77 | * [.getAuthorizationCode()](#OAuthMeteorModel+getAuthorizationCode) ⇒
78 | * [.saveAuthorizationCode(code, client, user)](#OAuthMeteorModel+saveAuthorizationCode) ⇒ Promise.<Object>
79 | * [.revokeAuthorizationCode()](#OAuthMeteorModel+revokeAuthorizationCode)
80 | * [.saveRefreshToken(token, clientId, expires, user)](#OAuthMeteorModel+saveRefreshToken) ⇒ Promise.<\*>
81 | * [.getRefreshToken()](#OAuthMeteorModel+getRefreshToken)
82 | * [.grantTypeAllowed(clientId, grantType)](#OAuthMeteorModel+grantTypeAllowed) ⇒ boolean
83 | * [.verifyScope(accessToken, scope)](#OAuthMeteorModel+verifyScope) ⇒ Promise.<boolean>
84 | * [.revokeToken()](#OAuthMeteorModel+revokeToken)
85 |
86 |
87 |
88 | ### oAuthMeteorModel.log(...args)
89 | Logs to console if debug is set to true
90 |
91 | **Kind**: instance method of [OAuthMeteorModel](#OAuthMeteorModel)
92 |
93 | | Param | Description |
94 | | --- | --- |
95 | | ...args | arbitrary list of params |
96 |
97 |
98 |
99 | ### oAuthMeteorModel.getAccessToken()
100 | getAccessToken(token) should return an object with:
101 | accessToken (String)
102 | accessTokenExpiresAt (Date)
103 | client (Object), containing at least an id property that matches the supplied client
104 | scope (optional String)
105 | user (Object)
106 |
107 | **Kind**: instance method of [OAuthMeteorModel](#OAuthMeteorModel)
108 |
109 |
110 | ### oAuthMeteorModel.createClient(title, homepage, description, privacyLink, redirectUris, grants, clientId, secret) ⇒ Promise.<Object>
111 | Registers a new client app in the {Clients} collection
112 |
113 | **Kind**: instance method of [OAuthMeteorModel](#OAuthMeteorModel)
114 |
115 | | Param |
116 | | --- |
117 | | title |
118 | | homepage |
119 | | description |
120 | | privacyLink |
121 | | redirectUris |
122 | | grants |
123 | | clientId |
124 | | secret |
125 |
126 |
127 |
128 | ### oAuthMeteorModel.getClient()
129 | getClient(clientId, clientSecret) should return an object with, at minimum:
130 | redirectUris (Array)
131 | grants (Array)
132 |
133 | **Kind**: instance method of [OAuthMeteorModel](#OAuthMeteorModel)
134 |
135 |
136 | ### oAuthMeteorModel.saveToken()
137 | saveToken(token, client, user) and should return:
138 | accessToken (String)
139 | accessTokenExpiresAt (Date)
140 | client (Object)
141 | refreshToken (optional String)
142 | refreshTokenExpiresAt (optional Date)
143 | user (Object)
144 |
145 | **Kind**: instance method of [OAuthMeteorModel](#OAuthMeteorModel)
146 |
147 |
148 | ### oAuthMeteorModel.getAuthorizationCode() ⇒
149 | getAuthCode() was renamed to getAuthorizationCode(code) and should return:
150 | client (Object), containing at least an id property that matches the supplied client
151 | expiresAt (Date)
152 | redirectUri (optional String)
153 |
154 | **Kind**: instance method of [OAuthMeteorModel](#OAuthMeteorModel)
155 | **Returns**: An Object representing the authorization code and associated data.
156 |
157 |
158 | ### oAuthMeteorModel.saveAuthorizationCode(code, client, user) ⇒ Promise.<Object>
159 | should return an Object representing the authorization code and associated data.
160 |
161 | **Kind**: instance method of [OAuthMeteorModel](#OAuthMeteorModel)
162 |
163 | | Param |
164 | | --- |
165 | | code |
166 | | client |
167 | | user |
168 |
169 |
170 |
171 | ### oAuthMeteorModel.revokeAuthorizationCode()
172 | revokeAuthorizationCode(code) is required and should return true
173 |
174 | **Kind**: instance method of [OAuthMeteorModel](#OAuthMeteorModel)
175 |
176 |
177 | ### oAuthMeteorModel.saveRefreshToken(token, clientId, expires, user) ⇒ Promise.<\*>
178 | **Kind**: instance method of [OAuthMeteorModel](#OAuthMeteorModel)
179 |
180 | | Param |
181 | | --- |
182 | | token |
183 | | clientId |
184 | | expires |
185 | | user |
186 |
187 |
188 |
189 | ### oAuthMeteorModel.getRefreshToken()
190 | getRefreshToken(token) should return an object with:
191 | refreshToken (String)
192 | client (Object), containing at least an id property that matches the supplied client
193 | refreshTokenExpiresAt (optional Date)
194 | scope (optional String)
195 | user (Object)
196 |
197 | **Kind**: instance method of [OAuthMeteorModel](#OAuthMeteorModel)
198 |
199 |
200 | ### oAuthMeteorModel.grantTypeAllowed(clientId, grantType) ⇒ boolean
201 | **Kind**: instance method of [OAuthMeteorModel](#OAuthMeteorModel)
202 |
203 | | Param |
204 | | --- |
205 | | clientId |
206 | | grantType |
207 |
208 |
209 |
210 | ### oAuthMeteorModel.verifyScope(accessToken, scope) ⇒ Promise.<boolean>
211 | Compares expected scope from token with actual scope from request
212 |
213 | **Kind**: instance method of [OAuthMeteorModel](#OAuthMeteorModel)
214 |
215 | | Param |
216 | | --- |
217 | | accessToken |
218 | | scope |
219 |
220 |
221 |
222 | ### oAuthMeteorModel.revokeToken()
223 | revokeToken(refreshToken) is required and should return true
224 |
225 | **Kind**: instance method of [OAuthMeteorModel](#OAuthMeteorModel)
226 |
227 |
228 | ## OAuth2ServerDefaults : Object
229 | Default options, that are used to merge with the user
230 | defined options.
231 |
232 | **Kind**: global constant
233 |
234 |
235 | ## DefaultModelConfig : Object
236 | Default collection names for the model collections.
237 |
238 | **Kind**: global constant
239 |
240 |
241 | ## bind ⇒ function
242 | Binds a function to the Meteor environment and Fiber
243 |
244 | **Kind**: global constant
245 | **Returns**: function - the bound function
246 |
247 | | Param | Type |
248 | | --- | --- |
249 | | fn | function |
250 |
251 |
252 |
253 | ## createCollection ⇒ Mongo.Collection
254 | If the given collection is already created or cached, returns the collection
255 | or creates a new one.
256 |
257 | **Kind**: global constant
258 |
259 | | Param | Type |
260 | | --- | --- |
261 | | passedCollection | Mongo.Collection \| undefined |
262 | | collectionName | string |
263 |
264 |
265 |
266 | ## errorHandler
267 | Unifies error handling as http response.
268 | Defaults to a 500 response, unless further details were added.
269 |
270 | **Kind**: global constant
271 |
272 | | Param | Type | Description |
273 | | --- | --- | --- |
274 | | res | | |
275 | | options | Object | options with error information |
276 | | options.error | String | Error name |
277 | | options.logError | boolean | optional flag to log the erroe to the console |
278 | | options.description | String | Error description |
279 | | options.uri | String | Optional uri to redirect to when error occurs |
280 | | options.status | Number | Optional statuscode, defaults to 500 |
281 | | options.state | String | State object vor validation |
282 | | options.debug | Boolean \| undefined | State object vor validation |
283 | | options.originalError | Error \| undefined | original Error instance |
284 |
285 |
286 |
287 | ## isModelInterface ⇒ boolean
288 | Since we allow projects to implement their own model (while providing ours
289 | as drop-in) we still need to validate, whether they implement the model
290 | correctly.
291 |
292 | We duck-type check if the model implements the most important functions.
293 | Uses the following values to check:
294 | - 'getAuthorizationCode',
295 | - 'getClient',
296 | - 'getRefreshToken',
297 | - 'revokeAuthorizationCode',
298 | - 'saveAuthorizationCode',
299 | - 'saveRefreshToken',
300 | - 'saveToken',
301 | - 'getAccessToken'
302 | - 'revokeToken'
303 |
304 | **Kind**: global constant
305 | **Returns**: boolean - true if valid, otherwise false
306 |
307 | | Param | Type | Description |
308 | | --- | --- | --- |
309 | | model | Object | the model implementation |
310 |
311 |
312 |
313 | ## UserValidation
314 | Used to register handlers for different instances that validate users.
315 | This allows you to validate user access on a client-based level.
316 |
317 | **Kind**: global constant
318 |
319 | * [UserValidation](#UserValidation)
320 | * [.register(instance, validationHandler)](#UserValidation.register)
321 | * [.isValid(instance, handlerArgs)](#UserValidation.isValid) ⇒ \*
322 |
323 |
324 |
325 | ### UserValidation.register(instance, validationHandler)
326 | Registers a validation method that allows
327 | to validate users on custom logic.
328 |
329 | **Kind**: static method of [UserValidation](#UserValidation)
330 |
331 | | Param | Type | Description |
332 | | --- | --- | --- |
333 | | instance | [OAuth2Server](#OAuth2Server) | |
334 | | validationHandler | function | sync or async function that performs the validation |
335 |
336 |
337 |
338 | ### UserValidation.isValid(instance, handlerArgs) ⇒ \*
339 | Delegates `handlerArgs` to the registered validation handler.
340 |
341 | **Kind**: static method of [UserValidation](#UserValidation)
342 | **Returns**: \* - should return truthy/falsy value
343 |
344 | | Param | Type |
345 | | --- | --- |
346 | | instance | [OAuth2Server](#OAuth2Server) |
347 | | handlerArgs | \* |
348 |
349 |
350 |
351 | ## validateParams ⇒ boolean
352 | Abstraction that checks given query/body params against a given schema
353 |
354 | **Kind**: global constant
355 |
356 | | Param |
357 | | --- |
358 | | actualParams |
359 | | requiredParams |
360 | | debug |
361 |
362 |
363 |
364 | ## app : Object
365 | Wrapped `WebApp` with express-style get/post and default use routes.
366 |
367 | **Kind**: global constant
368 | **See**: https://docs.meteor.com/packages/webapp.html
369 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | * Focusing on what is best not just for us as individuals, but for the
26 | overall community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | * The use of sexualized language or imagery, and sexual attention or
31 | advances of any kind
32 | * Trolling, insulting or derogatory comments, and personal or political attacks
33 | * Public or private harassment
34 | * Publishing others' private information, such as a physical or email
35 | address, without their explicit permission
36 | * Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | into [at] jankuester [dot] com.
64 | All complaints will be reviewed and investigated promptly and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series
86 | of actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or
93 | permanent ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within
113 | the community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.0, available at
119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120 |
121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
122 | enforcement ladder](https://github.com/mozilla/diversity).
123 |
124 | [homepage]: https://www.contributor-covenant.org
125 |
126 | For answers to common questions about this code of conduct, see the FAQ at
127 | https://www.contributor-covenant.org/faq. Translations are available at
128 | https://www.contributor-covenant.org/translations.
129 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to this project
2 |
3 | Thank you for your interest in this project and your aims to improving it.
4 | This guide will give you the most important info on how to contribute properly
5 | in order to get your pull requests accepted.
6 |
7 | ## Disclose security vulnerabilities
8 |
9 | First things first:
10 | This project has strong security implications and we appreciate every help to
11 | improve security.
12 |
13 | **However, please read our [security policy](./SECURITY.md), before taking
14 | actions.**
15 |
16 |
17 |
18 | ## Guiding principles
19 |
20 | Before contributing to this project it is important to understand how this
21 | project and it's collaborators views itself regarding it's scope and purpose.
22 |
23 | ### OAuth2 standard compliance
24 |
25 | This project aims full standard compliance. All improvements on functionality,
26 | as well as security implications, are done in a way that the standard remains
27 | as the highest reference of choice.
28 |
29 | If you are not familiar with the OAuth2 standards, please consult at least the
30 | following documents:
31 |
32 | - [RFC 6749 - The OAuth 2.0 Authorization Framework](https://datatracker.ietf.org/doc/html/rfc6749)
33 | - [RFC 8252 - OAuth 2.0 for Native Apps](https://datatracker.ietf.org/doc/html/rfc8252)
34 |
35 | Extended readings:
36 |
37 | - [RFC 6819 - OAuth 2.0 Threat Model and Security Considerations](https://datatracker.ietf.org/doc/html/rfc6819)
38 | - [RFC 7636 - Proof Key for Code Exchange by OAuth Public Clients](https://datatracker.ietf.org/doc/html/rfc7636)
39 | - [RFC 7591 - OAuth 2.0 Dynamic Client Registration Protocol](https://datatracker.ietf.org/doc/html/rfc7591)
40 |
41 | ### Meteor specific
42 |
43 | All contributions should be Meteor-specific but general enough to allow custom `accounts-*` implementations.
44 |
45 | ### Reference integration
46 |
47 | All contributions should use `accounts-lea` as reference integration.
48 |
49 | The repos are:
50 |
51 | - https://github.com/leaonline/meteor-accounts-lea
52 | - https://github.com/leaonline/meteor-accounts-oauth-lea
53 | - https://github.com/leaonline/leaonline-accounts
54 |
55 | ## Development
56 |
57 | If you want to fix bugs or add new features, **please read this chapter and it's
58 | sections carefully!**
59 |
60 | ### No PR without issue
61 |
62 | Please make sure your commitment will be appreciated by first opening an issue
63 | and discuss, whether this is a useful addition to the project.
64 |
65 | ### Work on a bug or a new feature
66 |
67 | First, clone and install this project from source via
68 |
69 | ```bash
70 | $ git clone git@github.com:leaonline/oauth2-server.git
71 | $ cd oauth2-server
72 | $ cd test-proxy
73 | $ meteor npm install
74 | $ meteor npm run setup # requred to link package to test-proxy project
75 | ```
76 |
77 | From here you can run several scripts for development purposes:
78 |
79 | ```bash
80 | $ meteor cd test-proxy
81 | $ meteor npm run test # runs the tests once
82 | $ meteor npm run test:coverage # runs the tests including coverage
83 | $ meteor npm run lint # runs the linter
84 | $ meteor npm run build:docs # updates API.md
85 | ```
86 |
87 | To work on a new feature or a fix please create a new branch:
88 |
89 | ```bash
90 | $ git checkout -b feature-xyz # or fix-xyz
91 | ```
92 |
93 | ### Coding rules
94 |
95 | - Unit-testing: all features or bug fixes must be tested by specs
96 | - Documentation: all public API methods must be documented
97 | - StandardJs: linter mmuss pass
98 |
99 | ### Commit message convention
100 |
101 | We use a commit convention, inspired by [angular commit message format](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-commit-message-format)
102 | with ticket number at the end of summary:
103 |
104 | ```
105 | (): #
106 | ```
107 | Summary in present tense. Not capitalized. No period at the end.
108 | The and fields are mandatory, the () and # field is optional.
109 |
110 | ### Run the tests before committing
111 |
112 | Please always make sure your code is passing linter and tests **before
113 | committing**. By doing so you help to make reviews much easier and don't pollute
114 | the history with commits, that are solely targeting lint fixes.
115 |
116 | You can run the tests via
117 |
118 | ```bash
119 | $ npm run test
120 | ```
121 |
122 | or
123 |
124 | ```bash
125 | $ npm run test:coverage
126 | ```
127 |
128 | to see your coverage.
129 |
130 | ### Open a pull request (PR)
131 |
132 | Once you have implemented your changes and tested them locally, please open
133 | a [pull request](https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request).
134 |
135 | Note: sometimes a pull request (PR) is also referred to as merge request (MR).
136 |
137 | #### Fundamental PR requirements
138 |
139 | There are a few basic requirements for your pull request to become accepted:
140 |
141 | - Make sure to open your pull request to target the `development` branch and not
142 | `master`
143 | - Make sure you are working on a branch, other than `development`; usually you
144 | can name the branch after the feature or fix you want to provide
145 | - Resolve any merge conflicts (usually by keeping your branch updated with
146 | `development`)
147 | - Have a clear description on what the PR does, including any steps necessary
148 | for testing, reviewing, reproduction etc.
149 | - Link to the existing issue
150 | - Added functions or changed functions need to get documented in compliance with
151 | JSDoc
152 | - Make sure all CI Tests are passing
153 |
154 | Also make sure, to comply with the following list:
155 |
156 | - Do not work on `development` directly
157 | - Do not implement multiple features in one pull request (this includes bumping
158 | versions of dependencies that are not related to the PR/issue)
159 | - Do not bump the release version (unless you are a maintainer)
160 | - Do not edit the Changelog as this will be done after your PR is merged
161 | - Do not introduce tight dependencies to a certain package that has not been
162 | approved during the discussion in the issue
163 |
164 | #### Review process
165 |
166 | Finally your PR needs to pass the review process:
167 |
168 | - A certain amount of maintainers needs to review and accept your PR
169 | - Please **expect change requests**! They will occur and are intended to improve
170 | the overall code quality.
171 | - If your changes have been updated please re-assign the reviewer who asked for
172 | the changes
173 | - Once all reviewers have approved your PR it will be merged by one of the
174 | maintainers :tada:
175 |
176 | #### After merge
177 |
178 | Please delete your branch after merge.
179 |
--------------------------------------------------------------------------------
/HISTORY.md:
--------------------------------------------------------------------------------
1 | # History
2 |
3 | ### 6.0.0
4 | - Meteor 3 / Express compatibility
5 | - added scope verification in authenticated routes
6 | - improved internal logging
7 | - fix bug in validation for custom models
8 | - fix support for explicit `client.id` field
9 |
10 | ## 5.0.0
11 | - sync support for @node-oauth/oauth2-server 5.x by
12 |
13 | ## 4.2.1
14 | - this is a patch release, fixing a syntax error
15 | (that never got picked up, due to wrong linter config)
16 |
17 | ### Code fixes
18 |
19 | - fix(core): standard lint fixed
20 | - fix(core): oauth.js fix wrong syntax error in import
21 |
22 | ### Dev fixes
23 |
24 | - fix(ci): run npm setup before lint and test to link package
25 | - fix(build): remove .coverage from git
26 | (URLSearchParam missing s at the end)
27 | - fix(tests): test project linter settings fixed
28 | - fix(ci): remove dependencies from single job
29 | - update(ci): test workflow update to use test project
30 |
31 |
32 | ## 4.2.0
33 | - updated `@node-oauth/oauth2-server` to `4.2.0`
34 | - correctly setup coverage for test project and package
35 | - added documentation and generate docs via jsdoc2md (see [API.md](./API.md))
36 | - fix(core): extracted initRoutes from OAuth2Server into standalone function to
37 | prevent re-init
38 |
39 | ## 4.1.0
40 | - this version has not been released publicly and superseded by `4.2.0`
41 | - removed `redirectUris` to find client (now searches only by clientId) in
42 | `createClient`
43 | - hardened check failsafety in `UserValidation`
44 | - hardened check against empty Strings in `requiredAccessTokenPostParams`,
45 | `requiredAuthorizeGetParams` and `requiredAuthorizePostParams`
46 | - updated `@node-oauth/oauth2-server` to `4.1.1`
47 |
48 | ## 4.0.0
49 | - use (actively maintained) @node-oauth/oauth2-server
50 | - improve console output readability
51 | - support Meteor versions `['1.6', '2.3']`; because 2.3 was a breaking release
52 | - update tests to use latest Accounts and jkuester:http
53 | - update follow redirect rules
54 | - maybe-breaking due to update to Accounts 2.x
55 |
56 | ## 3.3.0
57 |
58 | - updated `oauth2-server` to `4.0.0-dev.3`
59 | - removed dependency to `dburles:mongo-collection-instances` (fix dependeny
60 | issues with major accounts packages bumps)
61 |
62 | ## 3.2.1
63 |
64 | - bumped `oauth2-server`
65 |
66 | ## 3.1.0 - 2019/10/09
67 |
68 | -
69 |
70 | ## 3.0.0 - 2019/10/01
71 |
72 | **Summary**
73 |
74 | - migrated to latest node oauth2 server (v3)
75 | - wrap model in async Meteor environment
76 | - use builtin `WebApp` instead of `express`
77 | - built complete `authorization_code` workflow to make this usable with a custom `Accounts` package
78 | - all routes fallback with an error handler to return the respective OAuth error
79 |
80 | **Commits**
81 |
82 | - lint fix to comply standardjs
83 | - oauth server implemented token handler and authenticated (token-required) routes
84 | - model implemented token adapters
85 | - model simplified logging
86 | - model fixed getClient async access and logging behavior
87 | - oauth fixed references to this via self property
88 | - redirect after authorizazion success
89 | - implement authorization stack
90 | - fix saveAuthorizationCode
91 | - model simplified functions and removed unneccesary Promises
92 | - model saveAuthorizationCode conform with node2 server 3.x API
93 | - model updated debug logging format
94 | - reusable validation for client_id and redirect_url
95 | - extended validation in POST authUrl
96 | - extended error handler
97 | - cleanup imports
98 | - use facaded app that can be replaced if desired
99 | - add input validation utils
100 | - use get and post route for WebApp connect
101 | - model im proved console.log
102 | - improved error handling on auth route
103 | - model improve debug messages
104 | - introduced oauth app registration
105 | - fixed lint errors
106 | - model rewritten using promises
107 | - update README
108 | - removed express dependency and v3 compatibility
109 | - extended model config and v3 compatibility
110 | - decaffeinated project
111 | - added .npm to gitignore
112 |
113 |
114 | ## 2.1.0 - 2019/07/11
115 |
116 | - Support multiple `redirect_uri` #10
117 |
118 | ## 2.0.0 - 2016/01/08
119 |
120 | - Rename all athorizedClients to authorizedClients (please update your users DB too)
121 | - Allow `refresh_token` as a Grant Type
122 | - Transform any requests to `/oauth/token` that is `POST` and isn't `application/x-www-form-urlencoded`, merging the body and the query strings. See [pull request #5](https://github.com/RocketChat/rocketchat-oauth2-server/pull/5) for more details.
123 |
124 | ## 1.4.0 - 2016/01/08
125 |
126 | - Redirect user to `/oauth/error/404` instead of `/oauth/404`
127 | - Redirect user to `/oauth/error/invalid_redirect_uri` if uri does not match
128 |
129 | ## 1.3.0 - 2016/01/08
130 |
131 | - Redirect user to `/oauth/404` if client does not exists or is inactive
132 |
133 | ## 1.2.0 - 2016/01/07
134 |
135 | - Return only clients with `active: true`
136 |
137 | ## 1.1.1 - 2015/01/06
138 |
139 | - Only process errors for oauth routes
140 |
141 | ## 1.1.0 - 2015/01/05
142 |
143 | - Allow pass collection object instead collection name
144 |
145 | ## 1.0.1 - 2015/12/31
146 |
147 | - Added more debug logs
148 |
149 | ## 1.0.0 - 2015/12/31
150 |
151 | - Initial implementation
152 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Rocket.Chat, 2019 Jan Küster
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Meteor OAuth2 Server
2 |
3 | [](https://github.com/leaonline/oauth2-server/actions/workflows/tests.yml)
4 | [](https://github.com/leaonline/oauth2-server/actions/workflows/codeql-analysis.yml)
5 | [](https://atmospherejs.com/leaonline/oauth2-server)
6 | [](https://standardjs.com)
7 | [](https://www.repostatus.org/#active)
8 | 
9 | [](https://zenodo.org/doi/10.5281/zenodo.10817036)
10 |
11 |
12 | This package is a implementation of the package
13 | [@node-oauth/oauth2-server](https://github.com/node-oauth/node-oauth2-server)
14 | for Meteor.
15 | It can run without `express` (we use Meteor's builtin `WebApp`) and implements
16 | the `authorization_code` workflow and works like the Facebook's OAuth popup.
17 |
18 | ## Changelog
19 |
20 | View the full changelog in the [history page](./HISTORY.md).
21 |
22 | ## Install
23 |
24 | This package is a full scale drop-in, so you just need to add it via
25 |
26 | ```bash
27 | $ meteor add leaonline:oauth2-server
28 | ```
29 |
30 | ## Implementation
31 |
32 | The package comes with a default config, so you can start immediately.
33 | However, we made it all configurable for you.
34 |
35 | You can change various flags, routes and expiration times and collection names.
36 | The following sections will show you how to setup the server with a full
37 | configuration.
38 |
39 | ### Server implementation
40 |
41 | The following example uses the full configuration.
42 | The used values represent the current default values.
43 |
44 | `server/oauth2server.js`
45 | ```javascript
46 | import { Meteor } from "meteor/meteor"
47 | import { OAuth2Server } from 'meteor/leaonline:oauth2-server'
48 |
49 | const oauth2server = new OAuth2Server({
50 | serverOptions: {
51 | addAcceptedScopesHeader: true,
52 | addAuthorizedScopesHeader: true,
53 | allowBearerTokensInQueryString: false,
54 | allowEmptyState: false,
55 | authorizationCodeLifetime: 300,
56 | accessTokenLifetime: 3600,
57 | refreshTokenLifetime: 1209600,
58 | allowExtendedTokenAttributes: false,
59 | requireClientAuthentication: true
60 | },
61 | model: {
62 | accessTokensCollectionName: 'oauth_access_tokens',
63 | refreshTokensCollectionName: 'oauth_refresh_tokens',
64 | clientsCollectionName: 'oauth_clients',
65 | authCodesCollectionName: 'oauth_auth_codes',
66 | debug: true
67 | },
68 | routes: {
69 | accessTokenUrl: '/oauth/token',
70 | authorizeUrl: '/oauth/authorize',
71 | errorUrl: '/oauth/error',
72 | fallbackUrl: '/oauth/*'
73 | }
74 | })
75 |
76 | // this is a "secret" route that is only accessed with
77 | // a valid token, which has been generated
78 | // by the authorization_code grant flow
79 | // You will have to implement it to allow your remote apps
80 | // to retrieve the user credentials after successful
81 | // authentication.
82 | oauth2server.authenticatedRoute().get('/oauth/ident', function (req, res, next) {
83 | const user = Meteor.users.findOne(req.data.user.id)
84 |
85 | res.writeHead(200, {
86 | 'Content-Type': 'application/json',
87 | })
88 | const body = JSON.stringify({
89 | id: user._id,
90 | login: user.username,
91 | email: user.emails[0].address,
92 | firstName: user.firstName,
93 | lastName: user.lastName,
94 | name: `${user.firstName} ${user.lastName}`
95 | })
96 | res.end(body)
97 | })
98 |
99 | // create some fallback for all undefined routes
100 | oauth2server.app.use('*', function (req, res, next) {
101 | res.writeHead(404)
102 | res.end('route not found')
103 | })
104 | ```
105 |
106 | ### Additional validation
107 |
108 | Often, you want to restrict who of your users can access which client / service.
109 | In order to decide to give permission or not, you can register a handler that
110 | receives the authenticated user and the client she aims to access:
111 |
112 | ```javascript
113 | oauth2server.validateUser(function({ user, client }) {
114 | // the following example uses alanning:roles to check, whether a user
115 | // has been assigned a role that indicates she can access the client.
116 | // It is up to you how you implement this logic. If all users can access
117 | // all registered clients, you can simply omit this call at all.
118 | const { clientId } = client
119 | const { _id } = user
120 |
121 | return Roles.userIsInRoles(_id, 'manage-app', clientId)
122 | })
123 | ```
124 |
125 |
126 |
127 | ### Client/Popup implementation
128 |
129 | You should install a router to handle client side routing independently
130 | from the WebApp routes. You can for example use:
131 |
132 | ```bash
133 | $ meteor add ostrio:flow-router-extra
134 | ```
135 |
136 | and then define a client route for your popup dialog (we use Blaze in this example
137 | but it will work with any of your preferred and loved frontends):
138 |
139 | `client/main.html`
140 | ```javascript
141 |
142 | authserver
143 |
144 |
145 |
146 | {{> yield}}
147 |
148 | ```
149 |
150 | `client/main.js`
151 | ```javascript
152 | import { FlowRouter } from 'meteor/ostrio:flow-router-extra'
153 | import './authorize.html'
154 | import './authorize'
155 | import './main.html'
156 |
157 | // Define the route to render the popup view
158 | FlowRouter.route('/dialog/oauth', {
159 | action: function (params, queryParams) {
160 | this.render('layout', 'authorize', queryParams)
161 | }
162 | })
163 | ```
164 |
165 | `client/authorize.js`
166 | ```javascript
167 | // Subscribe the list of already authorized clients
168 | // to auto accept
169 | Template.authorize.onCreated(function() {
170 | this.subscribe('authorizedOAuth');
171 | });
172 |
173 | // Get the login token to pass to oauth
174 | // This is the best way to identify the logged user
175 | Template.authorize.helpers({
176 | getToken: function() {
177 | return localStorage.getItem('Meteor.loginToken');
178 | }
179 | });
180 |
181 | // Auto click the submit/accept button if user already
182 | // accepted this client
183 | Template.authorize.onRendered(function() {
184 | var data = this.data;
185 | this.autorun(function(c) {
186 | var user = Meteor.user();
187 | if (user && user.oauth && user.oauth.authorizedClients && user.oauth.authorizedClients.indexOf(data.client_id()) > -1) {
188 | c.stop();
189 | $('button').click();
190 | }
191 | });
192 | });
193 | ```
194 |
195 | `client/authorize.html`
196 | ```html
197 |
198 | {{#if currentUser}}
199 |
208 | {{#unless Template.subscriptionsReady}}
209 | loading...
210 | {{/unless}}
211 | {{else}}
212 | {{> loginButtons}}
213 | {{/if}}
214 |
215 | ```
216 |
217 | `client/style.css`
218 | ```css
219 | .hidden {
220 | display: none;
221 | }
222 | ```
223 |
224 | ## API and Documentation
225 |
226 | We also have an [API documentation](./API.md) with further info on the
227 | package internals.
228 |
229 | Furthermore we suggest you to consult the RFC docs on OAuth2:
230 |
231 | - [RFC 6749 - The OAuth 2.0 Authorization Framework](https://datatracker.ietf.org/doc/html/rfc6749.html)
232 | - [RFC 6750 - The OAuth 2.0 Authorization Framework: Bearer Token Usage](https://datatracker.ietf.org/doc/html/rfc6750.html)
233 |
234 | ## Testing
235 |
236 | We use mocha with `meteortesting:mocha` to run the tests.
237 | We have now a full scale test project inside this one and you can use it
238 | extensively to lint and test this project.
239 |
240 | ### Setup
241 |
242 | The setup is already prepared, so you just need to run a few commands:
243 |
244 | ```bash
245 | $ cd test-proxy
246 | $ meteor npm install # install npm dependencies
247 | $ meteor npm run setup # link with package
248 | ```
249 |
250 | ### Run the linter
251 |
252 | After the setup from the previous section you can run the linter via
253 |
254 | ```bash
255 | $ meteor npm run lint
256 | ```
257 |
258 | or auto-fix code via
259 |
260 | ```bash
261 | $ meteor npm run lint:fix
262 | ```
263 |
264 | Note, that we use `standardx`, which is `standard` code style with a few extra
265 | tweaks. We also use `eslint-plugin-security`, which can sometimes create lots
266 | of false-positives. If you need assistance, feel free to create an issue.
267 |
268 | ### Run the tests
269 |
270 | After the setup from the previous section you can run the tests via
271 |
272 | ```bash
273 | $ meteor npm run test
274 | ```
275 |
276 | or in watch mode via
277 |
278 | ```bash
279 | $ meteor npm run test:watch
280 | ```
281 |
282 | or with coverage report (+ watch mode) via
283 |
284 | ```bash
285 | $ meteor npm run test:coverage
286 | ```
287 |
288 | ### Build the docs
289 |
290 | We use jsDoc and jsdoc2md to create a markdown file. To build the docs use
291 |
292 | ```bash
293 | $ meteor npm run build:docs
294 | ```
295 |
296 | ## License
297 |
298 | MIT, see [license file](./LICENSE)
299 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | | Version | Supported |
6 | | ------- | ------------------ |
7 | | 4.x | :white_check_mark: |
8 | | 3.x | :white_check_mark: but only very critical security issues |
9 |
10 | ## Reporting a Vulnerability
11 |
12 | Report security vulnerabilities to info [at] jankuester [dot] com
13 |
14 | Please specify exactly how the vulnerability is to be exploited so we can estimate how severe the consequences can be (unless you also can specify them, too).
15 |
16 | Please note that we need to reproduce the vulnerability (as like with bugs) in order to safely fix it.
17 |
18 | A fix will be implemented in private until we can ensure the vulnerability is closed. A new release will immediately be published. If you want to provide a fix please let us know in the e-mail so we can setup a completely private repository to work on it together.
19 |
20 | Finally, all security fixes will also require to pass all tests and audits.
21 |
--------------------------------------------------------------------------------
/lib/defaults.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Default options, that are used to merge with the user
3 | * defined options.
4 | * @type {{serverOptions: {addAcceptedScopesHeader: boolean, addAuthorizedScopesHeader: boolean, allowBearerTokensInQueryString: boolean, allowEmptyState: boolean, authorizationCodeLifetime: number, accessTokenLifetime: number, refreshTokenLifetime: number, allowExtendedTokenAttributes: boolean, requireClientAuthentication: boolean}, responseTypes: {}, model: {accessTokensCollectionName: string, refreshTokensCollectionName: string, clientsCollectionName: string, authCodesCollectionName: string, debug: boolean}, routes: {accessTokenUrl: string, authorizeUrl: string, errorUrl: string, fallbackUrl: string}}}
5 | */
6 | export const OAuth2ServerDefaults = {
7 | serverOptions: {
8 | addAcceptedScopesHeader: true,
9 | addAuthorizedScopesHeader: true,
10 | allowBearerTokensInQueryString: false,
11 | allowEmptyState: false,
12 | authorizationCodeLifetime: 300,
13 | accessTokenLifetime: 3600,
14 | refreshTokenLifetime: 1209600,
15 | allowExtendedTokenAttributes: false,
16 | requireClientAuthentication: true
17 | },
18 | responseTypes: {
19 |
20 | },
21 | model: {
22 | accessTokensCollectionName: 'oauth_access_tokens',
23 | refreshTokensCollectionName: 'oauth_refresh_tokens',
24 | clientsCollectionName: 'oauth_clients',
25 | authCodesCollectionName: 'oauth_auth_codes',
26 | debug: false
27 | },
28 | routes: {
29 | accessTokenUrl: '/oauth/token',
30 | authorizeUrl: '/oauth/authorize',
31 | errorUrl: '/oauth/error',
32 | fallbackUrl: '/oauth/*'
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/lib/middleware/getDebugMiddleware.js:
--------------------------------------------------------------------------------
1 | import { debug } from '../utils/console'
2 |
3 | /**
4 | * Creates a middleware to debug routes on an instance level
5 | * @private
6 | * @param instance
7 | * @param options {object?} optional options
8 | * @param options.description {string?} optional way to descrive the next handler
9 | * @param options.data {boolean?} optional flag to log body/query
10 | */
11 | export const getDebugMiddleWare = (instance, options = {}) => {
12 | if (!instance.debug) {
13 | return function (req, res, next) { next() }
14 | }
15 |
16 | return function (req, res, next) {
17 | const baseUrl = req.originalUrl.split('?')[0]
18 | let message = `${req.method} ${baseUrl}`
19 |
20 | if (options.description) {
21 | message = `${message} (${options.description})`
22 | }
23 |
24 | if (options.data) {
25 | const data = { query: req.query, body: req.body }
26 | message = `${message} data: ${data}`
27 | }
28 |
29 | debug(message)
30 | next()
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/lib/middleware/secureHandler.js:
--------------------------------------------------------------------------------
1 | import { errorHandler } from '../utils/error'
2 | import { bind } from '../utils/bind'
3 |
4 | /**
5 | * Creates a failsafe route handler to prevent server-halting errors
6 | * @private
7 | * @param self
8 | * @param handler
9 | * @return {Function}
10 | */
11 | export const secureHandler = (self, handler) => bind(async function (req, res, next) {
12 | const that = this
13 | try {
14 | return handler.call(that, req, res, next)
15 | } catch (anyError) {
16 | // to avoid server-crashes we wrap all request handlers and
17 | // catch the error here, creating a default 500 response
18 | const state = req && req.query && req.query.state
19 | errorHandler(res, {
20 | error: 'server_error',
21 | status: 500,
22 | description: 'An internal server error occurred',
23 | state,
24 | debug: self.debug,
25 | originalError: anyError
26 | })
27 | }
28 | })
29 |
--------------------------------------------------------------------------------
/lib/model/DefaultModelConfig.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Default collection names for the model collections.
3 | * @type {{accessTokensCollectionName: string, refreshTokensCollectionName: string, clientsCollectionName: string, authCodesCollectionName: string, debug: boolean}}
4 | */
5 | export const DefaultModelConfig = {
6 | accessTokensCollectionName: 'oauth_access_tokens',
7 | refreshTokensCollectionName: 'oauth_refresh_tokens',
8 | clientsCollectionName: 'oauth_clients',
9 | authCodesCollectionName: 'oauth_auth_codes',
10 | debug: false
11 | }
12 |
--------------------------------------------------------------------------------
/lib/model/meteor-model.js:
--------------------------------------------------------------------------------
1 | import { Random } from 'meteor/random'
2 |
3 | export const collections = {
4 | /** @type {Mongo.Collection} */
5 | AccessTokens: undefined,
6 | /** @type {Mongo.Collection} */
7 | RefreshTokens: undefined,
8 | /** @type {Mongo.Collection} */
9 | Clients: undefined,
10 | /** @type {Mongo.Collection} */
11 | AuthCodes: undefined
12 | }
13 |
14 | /**
15 | * used by OAuthMeteorModel.prototype.getAccessToken
16 | * @private
17 | */
18 |
19 | export const getAccessToken = async (accessToken) => {
20 | return collections.AccessTokens.findOneAsync({ accessToken })
21 | }
22 |
23 | /**
24 | * used by OAuthMeteorModel.prototype.createClient
25 | * @private
26 | */
27 |
28 | export const createClient = async ({ title, homepage, description, privacyLink, redirectUris, grants, clientId, secret }) => {
29 | const existingClient = await collections.Clients.findOneAsync({ title })
30 |
31 | if (existingClient) {
32 | const updateValues = { description, privacyLink, redirectUris, grants }
33 | if (clientId) updateValues.clientId = clientId
34 | if (secret) updateValues.secret = secret
35 | return collections.Clients.updateAsync(existingClient._id, {
36 | $set: updateValues
37 | })
38 | }
39 | const id = clientId ?? Random.id(16)
40 | const clientDocId = await collections.Clients.insertAsync({
41 | title,
42 | homepage,
43 | description,
44 | privacyLink,
45 | redirectUris,
46 | clientId: id,
47 | id, // required by oauth-2-server which
48 | secret: secret || Random.id(32),
49 | grants
50 | })
51 | return collections.Clients.findOneAsync(clientDocId)
52 | }
53 |
54 | /**
55 | * used by OAuthMeteorModel.prototype.getClient
56 | * @private
57 | */
58 |
59 | export const getClient = async (clientId, secret) => {
60 | const clientDoc = await collections.Clients.findOneAsync({
61 | clientId,
62 | secret: secret || undefined // secret can be undefined or null but should act as the same
63 | })
64 | return clientDoc || false
65 | }
66 |
67 | /**
68 | * used by OAuthMeteorModel.prototype.saveToken
69 | * @private
70 | */
71 |
72 | export const saveToken = async (tokenDoc, clientDoc, userDoc) => {
73 | const tokenDocId = await collections.AccessTokens.insertAsync({
74 | accessToken: tokenDoc.accessToken,
75 | accessTokenExpiresAt: tokenDoc.accessTokenExpiresAt,
76 | refreshToken: tokenDoc.refreshToken,
77 | refreshTokenExpiresAt: tokenDoc.refreshTokenExpiresAt,
78 | scope: tokenDoc.scope,
79 | client: {
80 | id: clientDoc.clientId
81 | },
82 | user: {
83 | id: userDoc.id
84 | }
85 | })
86 | return collections.AccessTokens.findOneAsync(tokenDocId)
87 | }
88 |
89 | /**
90 | * used by OAuthMeteorModel.prototype.getAuthorizationCode
91 | * @private
92 | */
93 |
94 | export const getAuthorizationCode = async (authorizationCode) => {
95 | return collections.AuthCodes.findOneAsync({ authorizationCode })
96 | }
97 |
98 | /**
99 | * used by OAuthMeteorModel.prototype.saveAuthorizationCode
100 | * @private
101 | */
102 |
103 | export const saveAuthorizationCode = async (code, client, user) => {
104 | const { authorizationCode } = code
105 | const { expiresAt } = code
106 | const { redirectUri } = code
107 |
108 | await collections.AuthCodes.upsertAsync({ authorizationCode }, {
109 | authorizationCode,
110 | expiresAt,
111 | redirectUri,
112 | scope: code.scope,
113 | client: {
114 | // xxx: fix for newer oauth2-server versions
115 | id: client.id ?? client.clientId
116 | },
117 | user: {
118 | id: user.id
119 | }
120 | })
121 | return collections.AuthCodes.findOneAsync({ authorizationCode })
122 | }
123 |
124 | /**
125 | * used by OAuthMeteorModel.prototype.revokeAuthorizationCode
126 | * @private
127 | */
128 |
129 | export const revokeAuthorizationCode = async ({ authorizationCode }) => {
130 | const docCount = await collections.AuthCodes.countDocuments({ authorizationCode })
131 | if (docCount === 0) {
132 | return true
133 | }
134 | const removeCount = await collections.AuthCodes.removeAsync({ authorizationCode })
135 | return removeCount === docCount
136 | }
137 |
138 | /**
139 | * used by OAuthMeteorModel.prototype.saveRefreshToken
140 | * @private
141 | */
142 |
143 | export const saveRefreshToken = async (token, clientId, expires, user) => {
144 | return collections.RefreshTokens.insertAsync({
145 | refreshToken: token,
146 | clientId,
147 | userId: user.id,
148 | expires
149 | })
150 | }
151 |
152 | /**
153 | * used by OAuthMeteorModel.prototype.getRefreshToken
154 | * @private
155 | */
156 | export const getRefreshToken = async (refreshToken) => {
157 | return collections.RefreshTokens.findOneAsync({ refreshToken })
158 | }
159 |
160 | export const revokeToken = async (token) => {
161 | const result = await collections.AccessTokens.removeAsync({ refreshToken: token.refreshToken })
162 | return !!result
163 | }
164 |
--------------------------------------------------------------------------------
/lib/model/model.js:
--------------------------------------------------------------------------------
1 | import { DefaultModelConfig } from './DefaultModelConfig'
2 | import { createCollection } from '../utils/createCollection'
3 | import {
4 | collections,
5 | createClient,
6 | getAuthorizationCode,
7 | getClient,
8 | getRefreshToken,
9 | revokeAuthorizationCode,
10 | saveAuthorizationCode,
11 | saveRefreshToken,
12 | saveToken,
13 | getAccessToken,
14 | revokeToken
15 | } from './meteor-model'
16 |
17 | /**
18 | * Implements the OAuth2Server model with Meteor-Mongo bindings.
19 | */
20 | class OAuthMeteorModel {
21 | constructor (config = {}) {
22 | const modelConfig = { ...DefaultModelConfig, ...config }
23 | this.debug = modelConfig.debug
24 | collections.AccessTokens = createCollection(modelConfig.accessTokensCollection, modelConfig.accessTokensCollectionName)
25 | collections.RefreshTokens = createCollection(modelConfig.refreshTokensCollection, modelConfig.refreshTokensCollectionName)
26 | collections.AuthCodes = createCollection(modelConfig.authCodesCollection, modelConfig.authCodesCollectionName)
27 | collections.Clients = createCollection(modelConfig.clientsCollection, modelConfig.clientsCollectionName)
28 | }
29 |
30 | /**
31 | * Logs to console if debug is set to true
32 | * @param args arbitrary list of params
33 | */
34 |
35 | log (...args) {
36 | if (this.debug === true) {
37 | console.debug('[OAuth2Server][model]:', ...args)
38 | }
39 | }
40 |
41 | /**
42 | getAccessToken(token) should return an object with:
43 | accessToken (String)
44 | accessTokenExpiresAt (Date)
45 | client (Object), containing at least an id property that matches the supplied client
46 | scope (optional String)
47 | user (Object)
48 | */
49 | async getAccessToken (bearerToken) {
50 | this.log(`getAccessToken (bearerToken: '${bearerToken}')`)
51 | return getAccessToken(bearerToken)
52 | }
53 |
54 | /**
55 | * Registers a new client app in the {Clients} collection
56 | * @param title
57 | * @param homepage
58 | * @param description
59 | * @param privacyLink
60 | * @param redirectUris
61 | * @param grants
62 | * @param clientId
63 | * @param secret
64 | * @return {Promise