├── .gitignore ├── examples ├── clientApplication │ ├── .gitignore │ ├── .meteor │ │ ├── .gitignore │ │ ├── release │ │ ├── platforms │ │ ├── .id │ │ ├── .finished-upgraders │ │ ├── packages │ │ └── versions │ ├── packages │ │ └── meteor-oauth2-client │ ├── client │ │ ├── index.css │ │ ├── configFormFixes.js │ │ ├── index.js │ │ └── index.html │ ├── README.md │ ├── LICENSE.txt │ └── server │ │ └── index.js ├── resourceServer │ ├── .gitignore │ ├── .meteor │ │ ├── .gitignore │ │ ├── release │ │ ├── platforms │ │ ├── .id │ │ ├── .finished-upgraders │ │ ├── packages │ │ └── versions │ ├── packages │ │ └── meteor-oauth2-server │ ├── client │ │ ├── index.css │ │ ├── index.js │ │ └── index.html │ ├── README.md │ ├── server │ │ ├── methods.js │ │ └── rest.js │ └── LICENSE.txt └── README.md ├── packages ├── meteor-oauth2-server │ ├── .gitignore │ ├── .versions │ ├── package.js │ ├── client.js │ ├── LICENSE.txt │ ├── common.js │ ├── README.md │ ├── server.js │ └── meteor-model.js └── meteor-oauth2-client │ ├── meteor_configure.js │ ├── meteor_common.js │ ├── meteor_configure.html │ ├── LICENSE │ ├── package.js │ ├── .versions │ ├── meteor_client.js │ ├── README.md │ └── meteor_server.js ├── documentation ├── OAuthWebSequence.png └── OAuthWebSequenceWithConfig.png └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .npm 2 | -------------------------------------------------------------------------------- /examples/clientApplication/.gitignore: -------------------------------------------------------------------------------- 1 | .idea -------------------------------------------------------------------------------- /examples/resourceServer/.gitignore: -------------------------------------------------------------------------------- 1 | .idea -------------------------------------------------------------------------------- /examples/resourceServer/.meteor/.gitignore: -------------------------------------------------------------------------------- 1 | local 2 | -------------------------------------------------------------------------------- /examples/clientApplication/.meteor/.gitignore: -------------------------------------------------------------------------------- 1 | local 2 | -------------------------------------------------------------------------------- /examples/resourceServer/.meteor/release: -------------------------------------------------------------------------------- 1 | METEOR@1.2.1 2 | -------------------------------------------------------------------------------- /examples/clientApplication/.meteor/release: -------------------------------------------------------------------------------- 1 | METEOR@1.2.1 2 | -------------------------------------------------------------------------------- /packages/meteor-oauth2-server/.gitignore: -------------------------------------------------------------------------------- 1 | .npm 2 | .idea 3 | -------------------------------------------------------------------------------- /examples/clientApplication/.meteor/platforms: -------------------------------------------------------------------------------- 1 | server 2 | browser 3 | -------------------------------------------------------------------------------- /examples/resourceServer/.meteor/platforms: -------------------------------------------------------------------------------- 1 | server 2 | browser 3 | -------------------------------------------------------------------------------- /examples/resourceServer/packages/meteor-oauth2-server: -------------------------------------------------------------------------------- 1 | ../../../packages/meteor-oauth2-server -------------------------------------------------------------------------------- /examples/clientApplication/packages/meteor-oauth2-client: -------------------------------------------------------------------------------- 1 | ../../../packages/meteor-oauth2-client/ -------------------------------------------------------------------------------- /examples/resourceServer/client/index.css: -------------------------------------------------------------------------------- 1 | .section { 2 | padding-left: 2em; 3 | padding-right: 2em; 4 | } -------------------------------------------------------------------------------- /examples/clientApplication/client/index.css: -------------------------------------------------------------------------------- 1 | .section { 2 | padding-left: 2em; 3 | padding-right: 2em; 4 | } -------------------------------------------------------------------------------- /documentation/OAuthWebSequence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prime-8-consulting/meteor-oauth2/HEAD/documentation/OAuthWebSequence.png -------------------------------------------------------------------------------- /documentation/OAuthWebSequenceWithConfig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prime-8-consulting/meteor-oauth2/HEAD/documentation/OAuthWebSequenceWithConfig.png -------------------------------------------------------------------------------- /examples/clientApplication/.meteor/.id: -------------------------------------------------------------------------------- 1 | # This file contains a token that is unique to your project. 2 | # Check it into your repository along with the rest of this directory. 3 | # It can be used for purposes such as: 4 | # - ensuring you don't accidentally deploy one app on top of another 5 | # - providing package authors with aggregated statistics 6 | 7 | ba5i7g1ib6dam3006bh 8 | -------------------------------------------------------------------------------- /examples/resourceServer/.meteor/.id: -------------------------------------------------------------------------------- 1 | # This file contains a token that is unique to your project. 2 | # Check it into your repository along with the rest of this directory. 3 | # It can be used for purposes such as: 4 | # - ensuring you don't accidentally deploy one app on top of another 5 | # - providing package authors with aggregated statistics 6 | 7 | okjv8s177qxaw1qvqjsj 8 | -------------------------------------------------------------------------------- /examples/clientApplication/.meteor/.finished-upgraders: -------------------------------------------------------------------------------- 1 | # This file contains information which helps Meteor properly upgrade your 2 | # app when you run 'meteor update'. You should check it into version control 3 | # with your project. 4 | 5 | notices-for-0.9.0 6 | notices-for-0.9.1 7 | 0.9.4-platform-file 8 | notices-for-facebook-graph-api-2 9 | 1.2.0-standard-minifiers-package 10 | 1.2.0-meteor-platform-split 11 | 1.2.0-cordova-changes 12 | 1.2.0-breaking-changes 13 | -------------------------------------------------------------------------------- /examples/resourceServer/.meteor/.finished-upgraders: -------------------------------------------------------------------------------- 1 | # This file contains information which helps Meteor properly upgrade your 2 | # app when you run 'meteor update'. You should check it into version control 3 | # with your project. 4 | 5 | notices-for-0.9.0 6 | notices-for-0.9.1 7 | 0.9.4-platform-file 8 | notices-for-facebook-graph-api-2 9 | 1.2.0-standard-minifiers-package 10 | 1.2.0-meteor-platform-split 11 | 1.2.0-cordova-changes 12 | 1.2.0-breaking-changes 13 | -------------------------------------------------------------------------------- /packages/meteor-oauth2-client/meteor_configure.js: -------------------------------------------------------------------------------- 1 | Template.configureLoginServiceDialogForMeteorOAuth2Server.base_url = function () { 2 | return Meteor.absoluteUrl(); 3 | }; 4 | 5 | Template.configureLoginServiceDialogForMeteorOAuth2Server.fields = function () { 6 | return [ 7 | { property: 'clientId', label: 'API Key (Client ID)' }, 8 | { property: 'secret', label: 'Secret Key' }, 9 | { property: 'baseUrl', label: 'Target Base URL' }, 10 | { property: 'loginUrl', label: 'Target Login URL' } 11 | ]; 12 | }; 13 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | This is our directory of examples. 2 | 3 | ## resourceServer 4 | This is an example of using the meteor-oauth2-server package. If you want to make your meteor 5 | application provide oauth services to other site, this should be your reference porint. This 6 | application is also used as a test application to ensure the meteor-oauth2-server package is 7 | functioning properly. 8 | 9 | ## clientApplication 10 | This is an example of using the meteor-oauth2-client package. If you want users to authenticate from 11 | a meteor application that is running the meteor-oauth2-server package, this should be your reference 12 | point. This application assumes you will have the resourceServer application running. 13 | -------------------------------------------------------------------------------- /packages/meteor-oauth2-server/.versions: -------------------------------------------------------------------------------- 1 | babel-compiler@5.8.24_1 2 | babel-runtime@0.1.4 3 | base64@1.0.4 4 | blaze@2.1.3 5 | blaze-tools@1.0.4 6 | boilerplate-generator@1.0.4 7 | check@1.1.0 8 | deps@1.0.9 9 | diff-sequence@1.0.1 10 | ecmascript@0.1.6 11 | ecmascript-runtime@0.2.6 12 | ejson@1.0.7 13 | html-tools@1.0.5 14 | htmljs@1.0.5 15 | id-map@1.0.4 16 | jquery@1.11.4 17 | local-test:prime8consulting:meteor-oauth2-server@0.0.3 18 | logging@1.0.8 19 | meteor@1.1.10 20 | meteorhacks:async@1.0.0 21 | mongo-id@1.0.1 22 | observe-sequence@1.0.7 23 | prime8consulting:meteor-oauth2-server@0.0.3 24 | promise@0.5.1 25 | random@1.0.5 26 | reactive-var@1.0.6 27 | routepolicy@1.0.6 28 | simple:json-routes@2.1.0 29 | spacebars@1.0.7 30 | spacebars-compiler@1.0.7 31 | tracker@1.0.9 32 | ui@1.0.8 33 | underscore@1.0.4 34 | webapp@1.2.3 35 | webapp-hashing@1.0.5 36 | -------------------------------------------------------------------------------- /packages/meteor-oauth2-client/meteor_common.js: -------------------------------------------------------------------------------- 1 | if (_.isUndefined(MeteorOAuth2)) { 2 | MeteorOAuth2 = { 3 | serviceName: 'MeteorOAuth2Server' 4 | }; 5 | } 6 | 7 | Accounts.oauth.registerService(MeteorOAuth2.serviceName); 8 | 9 | if (Meteor.isClient) { 10 | Meteor['loginWith'+ MeteorOAuth2.serviceName] = function(options, callback) { 11 | if (! callback && typeof options === "function") { 12 | callback = options; 13 | options = null; 14 | } 15 | 16 | var credentialRequestCompleteCallback = Accounts.oauth.credentialRequestCompleteHandler(callback); 17 | MeteorOAuth2.requestCredential(options, credentialRequestCompleteCallback); 18 | }; 19 | } else { 20 | Accounts.addAutopublishFields({ 21 | forLoggedInUser: ['services.'+ MeteorOAuth2.serviceName], 22 | forOtherUsers: [] 23 | }); 24 | } -------------------------------------------------------------------------------- /examples/clientApplication/README.md: -------------------------------------------------------------------------------- 1 | ## Client Application 2 | 3 | This example is used to test and demonstrate the meteor-oauth2-client package 4 | and it's interaction with a meteor service running the 5 | meteor-oauth2-server package. This example expects that the resourceServer 6 | application is also running. Check the example directory for details. 7 | 8 | ========================================= 9 | #### Installation 10 | 11 | Starting: 12 | ``` sh 13 | meteor --port 3200 14 | ``` 15 | After it is started, goto http://localhost:3200 and walk through the steps. 16 | 17 | The reason we are using a specific port here is to: 18 | 1. Not interfere with your running instance of meteor. 19 | 2. Work well with the resourceServer example as it assumes this application 20 | will be hosted at localhost:3200 21 | 22 | ========================================= 23 | #### Licensing 24 | 25 | ![MIT License](https://img.shields.io/badge/license-MIT-blue.svg) 26 | -------------------------------------------------------------------------------- /packages/meteor-oauth2-client/meteor_configure.html: -------------------------------------------------------------------------------- 1 | 20 | -------------------------------------------------------------------------------- /packages/meteor-oauth2-server/package.js: -------------------------------------------------------------------------------- 1 | Package.describe({ 2 | name: 'prime8consulting:meteor-oauth2-server', 3 | version: '0.0.3', 4 | summary: 'Add oauth2 server support to your application.', 5 | git: 'https://github.com/prime-8-consulting/meteor-oauth2/packages/meteor-oauth2-server' 6 | }); 7 | 8 | Package.onUse(function(api) { 9 | api.versionsFrom('1.0'); 10 | 11 | api.use('webapp', 'server'); 12 | api.use('check', 'server'); 13 | api.use('meteorhacks:async@1.0.0', 'server'); 14 | api.use('simple:json-routes@2.1.0', 'server'); 15 | 16 | api.addFiles('common.js', ['client', 'server']); 17 | api.addFiles('meteor-model.js', 'server'); 18 | api.addFiles('server.js', 'server'); 19 | api.addFiles('client.js', 'client'); 20 | 21 | api.export('oAuth2Server', ['client', 'server']); 22 | }); 23 | 24 | Npm.depends({ 25 | "express": "4.13.4", 26 | "body-parser": "1.14.2", 27 | "oauth2-server": "2.4.1" 28 | }); 29 | 30 | Package.onTest(function(api) { 31 | 32 | }); 33 | -------------------------------------------------------------------------------- /examples/resourceServer/README.md: -------------------------------------------------------------------------------- 1 | ## ResourceServer 2 | 3 | This example is used to test the meteor-oauth2-server package. 4 | It can also be used as a demonstration of how to integrate oauth2 5 | into your own application. The code is well documented, so it should 6 | be fairly easy to follow and implement your own solution. Pay 7 | particular attention to server/rest.js and client/index.js for how 8 | to create authorization codes and use access tokens in a rest service. 9 | 10 | ========================================= 11 | #### Installation 12 | 13 | Starting: 14 | 15 | ``` sh 16 | meteor --port 3100 17 | ``` 18 | After it is started, goto http://localhost:3100 and walk through the steps. 19 | 20 | The reason we are using a specific port here is to: 21 | 1. Not interfere with your running instance of meteor. 22 | 2. Work well with the clientApplication example as it assumes the resourceServer 23 | will be hosted at localhost:3100 24 | 25 | ========================================= 26 | #### Licensing 27 | 28 | ![MIT License](https://img.shields.io/badge/license-MIT-blue.svg) 29 | -------------------------------------------------------------------------------- /examples/resourceServer/server/methods.js: -------------------------------------------------------------------------------- 1 | Meteor.methods({ 2 | /** 3 | * OAUTH FLOW - Step C1.1 4 | * While you are not required to implement client addition in the same way, clients will have 5 | * to be available for the oauth2 process to work properly. That may mean running code on 6 | * Meteor.startup() to populate the db. 7 | * @param client 8 | */ 9 | 'addClient': function (client){ 10 | console.log('addClient', client); 11 | oAuth2Server.collections.client.upsert( 12 | { 13 | clientId: client.clientId 14 | }, 15 | { 16 | $set: client 17 | } 18 | ); 19 | }, 20 | 21 | /** 22 | * Exists purely for testing purposes. 23 | * @returns {any} 24 | */ 25 | 'clientCount': function() { 26 | return oAuth2Server.collections.client.find({}).count(); 27 | }, 28 | 29 | /** 30 | * Exists purely for testing purposes. 31 | */ 32 | 'deleteAllClients': function() { 33 | oAuth2Server.collections.client.remove({}); 34 | } 35 | }); 36 | -------------------------------------------------------------------------------- /examples/resourceServer/.meteor/packages: -------------------------------------------------------------------------------- 1 | # Meteor packages used by this project, one per line. 2 | # Check this file (and the other files in this directory) into your repository. 3 | # 4 | # 'meteor add' and 'meteor remove' will edit this file for you, 5 | # but you can also edit it by hand. 6 | 7 | meteor-base # Packages every Meteor app needs to have 8 | mobile-experience # Packages for a great mobile UX 9 | mongo # The database Meteor supports right now 10 | blaze-html-templates # Compile .html files into Meteor Blaze views 11 | session # Client-side reactive dictionary for your app 12 | jquery # Helpful client-side library 13 | tracker # Meteor's client-side reactive programming library 14 | 15 | standard-minifiers # JS/CSS minifiers run for production mode 16 | es5-shim # ECMAScript 5 compatibility for older browsers. 17 | ecmascript # Enable ECMAScript2015+ syntax in app code 18 | 19 | accounts-ui 20 | accounts-password 21 | reactive-var 22 | prime8consulting:meteor-oauth2-server 23 | http 24 | simple:json-routes -------------------------------------------------------------------------------- /packages/meteor-oauth2-server/client.js: -------------------------------------------------------------------------------- 1 | oAuth2Server.subscribeTo = { 2 | authCode: function() { 3 | return Meteor.subscribe(oAuth2Server.pubSubNames.authCodes); 4 | }, 5 | refreshTokens: function() { 6 | return Meteor.subscribe(oAuth2Server.pubSubNames.refreshTokens); 7 | } 8 | }; 9 | 10 | oAuth2Server.callMethod = { 11 | /** 12 | * 13 | * @param client_id : string - The client id. 14 | * @param redirect_uri : string - The Uri to goto after processing. 15 | * @param response_type : string - Oauth response type. 16 | * @param scope : string[] - An array of scopes. 17 | * @param state : string - A state variable provided by the client. It will be added onto the redirectToUri. 18 | * @param callback 19 | */ 20 | authCodeGrant: function(client_id, redirect_uri, response_type, scope, state, callback) { 21 | Meteor.call( 22 | oAuth2Server.methodNames.authCodeGrant, 23 | client_id, 24 | redirect_uri, 25 | response_type, 26 | scope, 27 | state, 28 | callback 29 | ); 30 | } 31 | }; 32 | 33 | -------------------------------------------------------------------------------- /examples/clientApplication/LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Prime 8 Consulting 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /examples/resourceServer/LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Prime 8 Consulting 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /packages/meteor-oauth2-client/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Prime 8 Consulting 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /packages/meteor-oauth2-server/LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Prime 8 Consulting 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /packages/meteor-oauth2-client/package.js: -------------------------------------------------------------------------------- 1 | Package.describe({ 2 | name: 'prime8consulting:meteor-oauth2-client', 3 | version: '0.0.2', 4 | summary: 'Add support for logging into a meteor site running the meteor-oauth2-server package.', 5 | git: 'https://github.com/prime-8-consulting/meteor-oauth2/packages/meteor-oauth2-client' 6 | }); 7 | 8 | Package.onUse(function(api) { 9 | api.versionsFrom('1.0'); 10 | 11 | api.use('accounts-base', ['client', 'server']); 12 | api.use('accounts-oauth', ['client', 'server']); 13 | api.use('oauth', ['client', 'server']); 14 | api.use('oauth2', ['client', 'server']); 15 | api.use('http', ['client', 'server']); 16 | api.use('service-configuration', ['client', 'server']); 17 | api.use('underscore', ['client', 'server']); 18 | api.use(['random', 'templating@1.0.11'], 'client'); 19 | 20 | api.addFiles([ 21 | 'meteor_configure.html', 22 | 'meteor_configure.js' 23 | ], 'client'); 24 | 25 | api.addFiles('meteor_common.js', ['client', 'server']); 26 | api.addFiles('meteor_server.js', 'server'); 27 | api.addFiles('meteor_client.js', 'client'); 28 | 29 | api.export('MeteorOAuth2'); 30 | }); 31 | 32 | Package.onTest(function(api) { 33 | 34 | }); 35 | -------------------------------------------------------------------------------- /packages/meteor-oauth2-server/common.js: -------------------------------------------------------------------------------- 1 | refreshTokensCollection = new Meteor.Collection('OAuth2RefreshTokens'); 2 | refreshTokensCollection.allow({ 3 | insert: function(userId, doc) { 4 | return Meteor.isServer && userId && userId === doc.userId; 5 | }, 6 | update: function(userId, doc, fieldNames, modifier) { 7 | return false; 8 | }, 9 | remove: function(userId, doc) { 10 | return userId && userId === doc.userId; 11 | } 12 | }); 13 | 14 | authCodesCollection = new Meteor.Collection('OAuth2AuthCodes'); 15 | authCodesCollection.allow({ 16 | insert: function(userId, doc) { 17 | return Meteor.isServer && userId && userId === doc.userId; 18 | }, 19 | update: function(userId, doc, fieldNames, modifier) { 20 | return false; 21 | }, 22 | remove: function(userId, doc) { 23 | return userId && userId === doc.userId; 24 | } 25 | }); 26 | 27 | oAuth2Server = { 28 | pubSubNames: { 29 | authCodes: 'oauth2/authCodes', 30 | refreshTokens: 'oauth2/refreshTokens' 31 | }, 32 | methodNames: { 33 | authCodeGrant: 'oauth2/authCodeGrant' 34 | }, 35 | collections: { 36 | refreshToken: refreshTokensCollection, 37 | authCode: authCodesCollection 38 | } 39 | }; -------------------------------------------------------------------------------- /examples/clientApplication/.meteor/packages: -------------------------------------------------------------------------------- 1 | # Meteor packages used by this project, one per line. 2 | # Check this file (and the other files in this directory) into your repository. 3 | # 4 | # 'meteor add' and 'meteor remove' will edit this file for you, 5 | # but you can also edit it by hand. 6 | 7 | meteor-base # Packages every Meteor app needs to have 8 | mobile-experience # Packages for a great mobile UX 9 | mongo # The database Meteor supports right now 10 | blaze-html-templates # Compile .html files into Meteor Blaze views 11 | session # Client-side reactive dictionary for your app 12 | jquery # Helpful client-side library 13 | tracker # Meteor's client-side reactive programming library 14 | 15 | standard-minifiers # JS/CSS minifiers run for production mode 16 | es5-shim # ECMAScript 5 compatibility for older browsers. 17 | ecmascript # Enable ECMAScript2015+ syntax in app code 18 | 19 | autopublish # Publish all data to the clients (for prototyping) 20 | insecure # Allow all DB writes from clients (for prototyping) 21 | prime8consulting:meteor-oauth2-client 22 | service-configuration 23 | accounts-ui 24 | oauth 25 | reactive-var 26 | http 27 | webapp 28 | -------------------------------------------------------------------------------- /examples/clientApplication/server/index.js: -------------------------------------------------------------------------------- 1 | Meteor.methods({ 2 | /** 3 | * Convenience method for clearing out the service configuration. This should never exist in production. 4 | */ 5 | resetServiceConfiguration: function() { 6 | ServiceConfiguration.configurations.remove({ 7 | service: MeteorOAuth2.serviceName // using the constant provided by the package, easy for refactoring. 8 | }); 9 | }, 10 | 11 | /** 12 | * AUTH FLOW - Step A7. 13 | * We have an access token. Get the user from the REST service. 14 | * This will perform a server-to-server request for the identification of the user. This method 15 | * is not one you will need to implement as the oauth2 client package does this for you. We are 16 | * doing it here to demonstrate each step of the oauth2 process. 17 | */ 18 | getUserId: function() { 19 | var user = Meteor.user(); 20 | var serviceConfig = ServiceConfiguration.configurations.findOne({ 21 | service: MeteorOAuth2.serviceName 22 | }); 23 | 24 | 25 | return HTTP.get( 26 | serviceConfig.baseUrl + '/api/getUserId', 27 | { 28 | params: { 29 | access_token: user.services.MeteorOAuth2Server.accessToken 30 | } 31 | } 32 | ); 33 | } 34 | }); 35 | -------------------------------------------------------------------------------- /packages/meteor-oauth2-client/.versions: -------------------------------------------------------------------------------- 1 | accounts-base@1.2.2 2 | accounts-oauth@1.1.8 3 | babel-compiler@5.8.24_1 4 | babel-runtime@0.1.4 5 | base64@1.0.4 6 | binary-heap@1.0.4 7 | blaze@2.1.3 8 | blaze-tools@1.0.4 9 | boilerplate-generator@1.0.4 10 | caching-compiler@1.0.0 11 | caching-html-compiler@1.0.2 12 | callback-hook@1.0.4 13 | check@1.1.0 14 | ddp@1.2.2 15 | ddp-client@1.2.1 16 | ddp-common@1.2.2 17 | ddp-rate-limiter@1.0.0 18 | ddp-server@1.2.2 19 | deps@1.0.9 20 | diff-sequence@1.0.1 21 | ecmascript@0.1.6 22 | ecmascript-runtime@0.2.6 23 | ejson@1.0.7 24 | geojson-utils@1.0.4 25 | html-tools@1.0.5 26 | htmljs@1.0.5 27 | http@1.1.1 28 | id-map@1.0.4 29 | jquery@1.11.4 30 | local-test:prime8consulting:meteor-oauth2-client@0.0.2 31 | localstorage@1.0.5 32 | logging@1.0.8 33 | meteor@1.1.10 34 | minifiers@1.1.7 35 | minimongo@1.0.10 36 | mongo@1.1.3 37 | mongo-id@1.0.1 38 | npm-mongo@1.4.39_1 39 | oauth@1.1.6 40 | oauth2@1.1.5 41 | observe-sequence@1.0.7 42 | ordered-dict@1.0.4 43 | prime8consulting:meteor-oauth2-client@0.0.2 44 | promise@0.5.1 45 | random@1.0.5 46 | rate-limit@1.0.0 47 | reactive-var@1.0.6 48 | reload@1.1.4 49 | retry@1.0.4 50 | routepolicy@1.0.6 51 | service-configuration@1.0.5 52 | spacebars@1.0.7 53 | spacebars-compiler@1.0.7 54 | templating@1.1.5 55 | templating-tools@1.0.0 56 | tracker@1.0.9 57 | ui@1.0.8 58 | underscore@1.0.4 59 | url@1.0.5 60 | webapp@1.2.3 61 | webapp-hashing@1.0.5 62 | -------------------------------------------------------------------------------- /examples/resourceServer/server/rest.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This is an example where we use the oAuth2 server to allow/deny access to our REST service. 3 | * In this example, we will use JsonRoutes. 4 | */ 5 | 6 | /** 7 | * First we define the middle-ware to use. This ensures the oauth2 server validation code 8 | * is ran whenever /api/* is accessed from our server. Doing this let's your rest service 9 | * take full advantage of oauth2 without all the boiler plate. 10 | * It is expected whenever a /api url is called, a "access_token" is present in the 11 | * query or the body. 12 | */ 13 | JsonRoutes.Middleware.use( 14 | '/api/*', 15 | oAuth2Server.oauthserver.authorise() // OAUTH FLOW - A7.1 16 | ); 17 | 18 | /** 19 | * OAUTH FLOW - A7.2 20 | * 21 | * Here is an example of querying for and returning data. At this point, the api path 22 | * has been validated by the oAuth2 server package. So the code can execute 23 | * un-encumbered. 24 | * Note: this is a REST service and it can be called from any server any where. That 25 | * being said, this server has no concept if your user is authenticated or not. This 26 | * is the point of the "access_token". To get the userId, you will have to look it up 27 | * in the access token collection. 28 | */ 29 | JsonRoutes.add('get', '/api/getUserId', function(req, res, next) { 30 | console.log('GET /api/getUserId'); 31 | 32 | var accessTokenStr = (req.params && req.params.access_token) || (req.query && req.query.access_token); 33 | var accessToken = oAuth2Server.collections.accessToken.findOne({accessToken: accessTokenStr}); 34 | 35 | JsonRoutes.sendResult(res, { 36 | data: accessToken.userId 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /examples/clientApplication/client/configFormFixes.js: -------------------------------------------------------------------------------- 1 | /* 2 | This file just contains convenient methods for auto-filling the oauth2 login form configuration. None 3 | of the code here is important for implementing oauth2 on your site. 4 | */ 5 | 6 | $(document).ready(function() { 7 | // update the fields when the dialog is dynamically added. 8 | $(document).on('DOMNodeInserted', '#configure-login-service-dialog', function(e) { 9 | populateServiceValues(e.target); 10 | }); 11 | 12 | // update the fields on page load. 13 | populateServiceValues($('#configure-login-service-dialog')); 14 | }); 15 | 16 | function populateServiceValues(target) { 17 | var setCount = 0; 18 | setCount += prePopulateValues(target, 'configure-login-service-dialog-clientId', 'clientApplication'); 19 | setCount += prePopulateValues(target, 'configure-login-service-dialog-secret', '12345'); 20 | setCount += prePopulateValues(target, 'configure-login-service-dialog-baseUrl', 'http://localhost:3100'); 21 | setCount += prePopulateValues(target, 'configure-login-service-dialog-loginUrl', 'http://localhost:3100'); 22 | 23 | // a hacky way to make the meteor configure interface make the save button 24 | // enabled. it only enables if it detects key up events in the input fields. 25 | if (setCount) { 26 | $('#configure-login-service-dialog-clientId').trigger('keyup', 17); 27 | } 28 | } 29 | 30 | function prePopulateValues(target, id, value) { 31 | var el = $(target).find('#' + id); 32 | if (!el.length) { 33 | return false; 34 | } 35 | 36 | if (!el.val()) { 37 | el.val(value); 38 | return true; 39 | } 40 | 41 | return false; 42 | } 43 | -------------------------------------------------------------------------------- /examples/clientApplication/.meteor/versions: -------------------------------------------------------------------------------- 1 | accounts-base@1.2.2 2 | accounts-oauth@1.1.8 3 | accounts-ui@1.1.6 4 | accounts-ui-unstyled@1.1.8 5 | autopublish@1.0.4 6 | autoupdate@1.2.4 7 | babel-compiler@5.8.24_1 8 | babel-runtime@0.1.4 9 | base64@1.0.4 10 | binary-heap@1.0.4 11 | blaze@2.1.3 12 | blaze-html-templates@1.0.1 13 | blaze-tools@1.0.4 14 | boilerplate-generator@1.0.4 15 | caching-compiler@1.0.0 16 | caching-html-compiler@1.0.2 17 | callback-hook@1.0.4 18 | check@1.1.0 19 | ddp@1.2.2 20 | ddp-client@1.2.1 21 | ddp-common@1.2.2 22 | ddp-rate-limiter@1.0.0 23 | ddp-server@1.2.2 24 | deps@1.0.9 25 | diff-sequence@1.0.1 26 | ecmascript@0.1.6 27 | ecmascript-runtime@0.2.6 28 | ejson@1.0.7 29 | es5-shim@4.1.14 30 | fastclick@1.0.7 31 | geojson-utils@1.0.4 32 | hot-code-push@1.0.0 33 | html-tools@1.0.5 34 | htmljs@1.0.5 35 | http@1.1.1 36 | id-map@1.0.4 37 | insecure@1.0.4 38 | jquery@1.11.4 39 | launch-screen@1.0.4 40 | less@2.5.1 41 | livedata@1.0.15 42 | localstorage@1.0.5 43 | logging@1.0.8 44 | meteor@1.1.10 45 | meteor-base@1.0.1 46 | minifiers@1.1.7 47 | minimongo@1.0.10 48 | mobile-experience@1.0.1 49 | mobile-status-bar@1.0.6 50 | mongo@1.1.3 51 | mongo-id@1.0.1 52 | npm-mongo@1.4.39_1 53 | oauth@1.1.6 54 | oauth2@1.1.5 55 | observe-sequence@1.0.7 56 | ordered-dict@1.0.4 57 | prime8consulting:meteor-oauth2-client@0.0.2 58 | promise@0.5.1 59 | random@1.0.5 60 | rate-limit@1.0.0 61 | reactive-dict@1.1.3 62 | reactive-var@1.0.6 63 | reload@1.1.4 64 | retry@1.0.4 65 | routepolicy@1.0.6 66 | service-configuration@1.0.5 67 | session@1.1.1 68 | spacebars@1.0.7 69 | spacebars-compiler@1.0.7 70 | standard-minifiers@1.0.2 71 | templating@1.1.5 72 | templating-tools@1.0.0 73 | tracker@1.0.9 74 | ui@1.0.8 75 | underscore@1.0.4 76 | url@1.0.5 77 | webapp@1.2.3 78 | webapp-hashing@1.0.5 79 | -------------------------------------------------------------------------------- /examples/resourceServer/.meteor/versions: -------------------------------------------------------------------------------- 1 | accounts-base@1.2.2 2 | accounts-password@1.1.4 3 | accounts-ui@1.1.6 4 | accounts-ui-unstyled@1.1.8 5 | autoupdate@1.2.4 6 | babel-compiler@5.8.24_1 7 | babel-runtime@0.1.4 8 | base64@1.0.4 9 | binary-heap@1.0.4 10 | blaze@2.1.3 11 | blaze-html-templates@1.0.1 12 | blaze-tools@1.0.4 13 | boilerplate-generator@1.0.4 14 | caching-compiler@1.0.0 15 | caching-html-compiler@1.0.2 16 | callback-hook@1.0.4 17 | check@1.1.0 18 | ddp@1.2.2 19 | ddp-client@1.2.1 20 | ddp-common@1.2.2 21 | ddp-rate-limiter@1.0.0 22 | ddp-server@1.2.2 23 | deps@1.0.9 24 | diff-sequence@1.0.1 25 | ecmascript@0.1.6 26 | ecmascript-runtime@0.2.6 27 | ejson@1.0.7 28 | email@1.0.8 29 | es5-shim@4.1.14 30 | fastclick@1.0.7 31 | geojson-utils@1.0.4 32 | hot-code-push@1.0.0 33 | html-tools@1.0.5 34 | htmljs@1.0.5 35 | http@1.1.1 36 | id-map@1.0.4 37 | jquery@1.11.4 38 | launch-screen@1.0.4 39 | less@2.5.1 40 | livedata@1.0.15 41 | localstorage@1.0.5 42 | logging@1.0.8 43 | meteor@1.1.10 44 | meteor-base@1.0.1 45 | meteorhacks:async@1.0.0 46 | minifiers@1.1.7 47 | minimongo@1.0.10 48 | mobile-experience@1.0.1 49 | mobile-status-bar@1.0.6 50 | mongo@1.1.3 51 | mongo-id@1.0.1 52 | npm-bcrypt@0.7.8_2 53 | npm-mongo@1.4.39_1 54 | observe-sequence@1.0.7 55 | ordered-dict@1.0.4 56 | prime8consulting:meteor-oauth2-server@0.0.3 57 | promise@0.5.1 58 | random@1.0.5 59 | rate-limit@1.0.0 60 | reactive-dict@1.1.3 61 | reactive-var@1.0.6 62 | reload@1.1.4 63 | retry@1.0.4 64 | routepolicy@1.0.6 65 | service-configuration@1.0.5 66 | session@1.1.1 67 | sha@1.0.4 68 | simple:json-routes@2.1.0 69 | spacebars@1.0.7 70 | spacebars-compiler@1.0.7 71 | srp@1.0.4 72 | standard-minifiers@1.0.2 73 | templating@1.1.5 74 | templating-tools@1.0.0 75 | tracker@1.0.9 76 | ui@1.0.8 77 | underscore@1.0.4 78 | url@1.0.5 79 | webapp@1.2.3 80 | webapp-hashing@1.0.5 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Meteor OAuth2 2 | 3 | This is the root repository of several meteor packages that give your application the ability to act as an OAuth2 server or login to a OAuth2 Meteor application. 4 | 5 | Checkout the packages for the package source. The examples will be very helpful in understanding what you can do with these packages. 6 | 7 | ========================================= 8 | #### OAuth Flow - Diagram 9 | 10 | ![OAuthWebSequenceWithConfig](https://github.com/prime-8-consulting/meteor-oauth2/blob/master/documentation/OAuthWebSequenceWithConfig.png) 11 | 12 | ========================================= 13 | #### OAuth Flow - Step By Step 14 | 15 | ```bash 16 | C1 Request Client ID 17 | C1.1 Generate Client ID # Meteor.call('oauth/addclient', newClient) 18 | C1.2 Return Client ID 19 | C2 Configure Client ID 20 | 21 | A1 Start App # Meteor.startup() 22 | A1.1 Launch Browser 23 | A1.1.1. Request Login 24 | A1.1.1.1 Create Login Page 25 | A1.1.1.2 Return to Login Page 26 | A2 Enter Login Details 27 | A2.1 Submit Login Details 28 | A2.1.1 Authenticate User 29 | A2.1.2 Redirect to Application 30 | A3 Intercept Redirect 31 | A4 Extract Auth Code 32 | A5 Get Access Token # HTTP.post('/oauth/token') 33 | A5.1 Generate Access Token # oAuth2Server.callMethod.authCodeGrant() 34 | A5.2 Return Access/Refresh Token # HTTP.post('/oauth/token') 35 | A6 Save Refresh Token # tokenResult.set(result.data) 36 | A7 Request Data # HTTP.get('/api/getUserId') 37 | A7.1 Check Access Token # oAuth2Server.oauthserver.authorise() 38 | A7.2 Return Data # JsonRoutes.add 39 | A8 Rest of Business Logic 40 | ``` 41 | 42 | 43 | ========================================= 44 | #### Licensing 45 | 46 | ![MIT License](https://img.shields.io/badge/license-MIT-blue.svg) 47 | -------------------------------------------------------------------------------- /examples/clientApplication/client/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | All the code here is to demonstrate the two-way communication between the resource server and 3 | this client application. None of the code here is important when implementing the client on 4 | you site. 5 | */ 6 | 7 | var getUserIdResult = new ReactiveVar(null); 8 | 9 | Template.main.helpers({ 10 | getUserAccessToken: function() { 11 | return getUserAccessToken(); 12 | }, 13 | 14 | getUserIdResult: function() { 15 | return getUserIdResult.get(); 16 | }, 17 | 18 | getUserOAuth2Id: function() { 19 | var user = Meteor.user(); 20 | 21 | if (!isOAuth2User(user)) { 22 | return; 23 | } 24 | 25 | return user.services.MeteorOAuth2Server.id; 26 | } 27 | }); 28 | 29 | Template.main.events({ 30 | /** 31 | * Wipe out all the configured services. 32 | */ 33 | 'click button.resetServiceConfiguration': function() { 34 | Meteor.call('resetServiceConfiguration'); 35 | }, 36 | 37 | /** 38 | * Perform a server-to-server request to get the user id on the resource server. This action exists 39 | * just to demonstrate the steps in the oauth2 process. The oauth2 client package does this for you 40 | * when logging in. 41 | */ 42 | 'click button.testLocalTokens': function() { 43 | if (getUserAccessToken()) { 44 | Meteor.call('getUserId', function(err, result) { 45 | console.log(result); 46 | // set the userId. 47 | getUserIdResult.set(result.data); 48 | }); 49 | } // if 50 | } // function 51 | }); 52 | 53 | /** 54 | * Determine if a user originates from an oauth2 login. 55 | * @param user 56 | * @returns {*} 57 | */ 58 | function isOAuth2User(user) { 59 | return user 60 | && user.services 61 | && user.services.MeteorOAuth2Server 62 | ; 63 | } 64 | 65 | /** 66 | * Get the user access token if it exists. 67 | * @returns {*} 68 | */ 69 | function getUserAccessToken() { 70 | var user = Meteor.user(); 71 | 72 | if (!isOAuth2User(user)) { 73 | return; 74 | } 75 | 76 | return user.services.MeteorOAuth2Server.accessToken; 77 | } 78 | -------------------------------------------------------------------------------- /examples/clientApplication/client/index.html: -------------------------------------------------------------------------------- 1 | 2 | clientApplication 3 | 4 | 5 | 6 |

Welcome to ClientApplication!

7 | {{> main}} 8 | 9 | 10 | 62 | -------------------------------------------------------------------------------- /packages/meteor-oauth2-client/meteor_client.js: -------------------------------------------------------------------------------- 1 | MeteorOAuth2.requestCredential = function (options, credentialRequestCompleteCallback) { 2 | if (!credentialRequestCompleteCallback && typeof options === 'function') { 3 | credentialRequestCompleteCallback = options; 4 | options = {}; 5 | } 6 | 7 | var config = ServiceConfiguration.configurations.findOne({ 8 | service: MeteorOAuth2.serviceName 9 | }); 10 | 11 | if (!config) { 12 | credentialRequestCompleteCallback 13 | && credentialRequestCompleteCallback( 14 | new ServiceConfiguration.ConfigError('Service not configured') 15 | ); 16 | return; 17 | } 18 | 19 | if (!config) { 20 | credentialRequestCompleteCallback 21 | && credentialRequestCompleteCallback( 22 | ServiceConfiguration.ConfigError('Service not configured') 23 | ); 24 | } 25 | 26 | if (!config.baseUrl) { 27 | credentialRequestCompleteCallback 28 | && credentialRequestCompleteCallback( 29 | ServiceConfiguration.ConfigError('Service found but it does not have a baseUrl configured.') 30 | ); 31 | } 32 | 33 | if (!config.loginUrl) { 34 | credentialRequestCompleteCallback 35 | && credentialRequestCompleteCallback( 36 | ServiceConfiguration.ConfigError('Service found but it does not have a loginUrl configured.') 37 | ); 38 | } 39 | 40 | var credentialToken = Random.secret(); 41 | 42 | // always need this to get user id from service. 43 | var requiredScope = ['email']; 44 | var scope = []; 45 | if (options.scope) 46 | scope = options.scope; 47 | scope = _.union(scope, requiredScope); 48 | 49 | 50 | var loginStyle = OAuth._loginStyle(MeteorOAuth2.serviceName, config, options); 51 | 52 | var loginUrl = config.loginUrl + 53 | '?response_type=code' + 54 | '&client_id=' + config.clientId + 55 | '&redirect_uri=' + OAuth._redirectUri(MeteorOAuth2.serviceName, config) + 56 | '&scope=' + scope.join(' ') + 57 | '&state=' + OAuth._stateParam(loginStyle, credentialToken); 58 | 59 | OAuth.launchLogin({ 60 | loginService: MeteorOAuth2.serviceName, 61 | loginStyle: loginStyle, 62 | loginUrl: loginUrl, 63 | credentialRequestCompleteCallback: credentialRequestCompleteCallback, 64 | credentialToken: credentialToken, 65 | popupOptions: { height: 600 } 66 | }); 67 | }; 68 | -------------------------------------------------------------------------------- /packages/meteor-oauth2-server/README.md: -------------------------------------------------------------------------------- 1 | Meteor OAuth2 Server 2 | === 3 | Use your meteor application as an oauth2 server. This package will give the 4 | developer the tools necessary to get oauth2 running easily. Look at the 5 | resourceServer example for a detailed demonstration on how to get things 6 | working. 7 | 8 | ## Installation 9 | `meteor add prime8consulting:meteor-oauth2-server` 10 | 11 | 12 | ## API 13 | 14 | oAuth2Server - Exported so all code in meteor can access the functionality. 15 | - pubSubNames 16 | - authCodes - Constant string representing the auth codes pub/sub. 17 | 18 | - refreshTokens - Constant string representing the refresh token pub/sub. 19 | 20 | - methodNames 21 | - authCodeGrant - Constant string representing th authCodeGran meteor method. 22 | 23 | - collections 24 | - refreshToken - Collection of the refresh tokens. 25 | 26 | - authCode - Collection of the authorization codes. 27 | 28 | - accessToken - (server) Collection of the access tokens. 29 | 30 | - client - (server) Collection of the clients authorized to use the oauth2 service. 31 | 32 | - oauthserver - (server) Underlying node-oauth2-server object used to handle the oauth2 requests and responses. 33 | 34 | - subscribeTo (client) 35 | - authCode - Wrapper function to subscribe to the auth code subscription. Returns a standard subscription handle. 36 | 37 | Example: 38 | ```javascript 39 | // subscribe to a user's authorization codes. 40 | oAuth2Server.subscribeTo.authCode(); 41 | ``` 42 | 43 | - refreshTokens - Wrapper function subscribe to the refresh tokens subscription. Returns a standard subscription handle. 44 | 45 | Example: 46 | ```javascript 47 | // subscribe to a user's refresh tokens. 48 | oAuth2Server.subscribeTo.refreshTokens(); 49 | ``` 50 | 51 | - callMethod (client) 52 | - authCodeGrant - Wrapper for Meteor.method to create an authorization code. This is an async function 53 | and a callback must be provided to be of any use. 54 | 55 | Example: 56 | ```javascript 57 | oAuth2Server.callMethod.authCodeGrant(client_id, redirect_uri, response_type, scope, state, function(err, authCodeGrantResult) { 58 | // see below for a description of authCodeGrantResult 59 | }); 60 | ``` 61 | 62 | authCodeGrantResult 63 | ```javascript 64 | { 65 | success: boolean, 66 | error: any, 67 | authorizationCode: string, 68 | redirectToUri: string 69 | } 70 | ``` 71 | 72 | ## Examples 73 | There is an example meteor application that demonstrates authentication from 74 | another meteor application. 75 | https://github.com/prime-8-consulting/meteor-oauth2/tree/master/examples 76 | 77 | Checkout the resourceServer example application for a full example on how this 78 | package can and should be used. Your can also run both examples side-by-side 79 | to watch the entire login workflow. -------------------------------------------------------------------------------- /packages/meteor-oauth2-client/README.md: -------------------------------------------------------------------------------- 1 | # Meteor OAuth2 client 2 | Used in conjunction with meteor-oauth2-server on the the target server, this 3 | package will allow your site to authenticate users on another meteor application. 4 | 5 | ## Usage 6 | 7 | ### Configuration 8 | Since every Meteor application is different, there is not a universal approach 9 | to get your site setup as an oauth client. If you operate the target server, 10 | you can add a client following the example provided on [this example](https://github.com/prime-8-consulting/meteor-oauth2/blob/master/examples/resourceServer/server/index.js). 11 | 12 | If you do not operate the target server, your best bet is to contact the site 13 | operator and provide them with your redirect uri. It will be /_oauth/meteor-oauth2-server. 14 | 15 | Once you have your client setup on the resource owner, you are ready to 16 | get things running on your application. 17 | 18 | #### Add a service configuration 19 | ##### The easy way 20 | The easiest solution is to use the meteor UI. After starting your application 21 | goto a page where the login button appear and click on the red configure button 22 | for MeteorOAuth2Server. 23 | 24 | ##### The difficult way 25 | Alternatively, you could manually create the service configuration 26 | Note: Looking for a better way to handle this. Perhaps using meteor's config files? File an issue with your ideas. 27 | ``` 28 | $ meteor add service-configuration 29 | ``` 30 | 31 | ``` javascript 32 | ServiceConfiguration.configurations.remove({ 33 | service: 'MeteorOAuth2Server' 34 | }); 35 | 36 | ServiceConfiguration.configurations.insert({ 37 | service: 'MeteorOAuth2Server', 38 | clientId: '{ your client id, provided by resource owner }', 39 | scope: [], // whatever scope the resource owner supports. By default, ['email'] will be used. 40 | secret: '{ your app\'s secret, provided by resource owner }', 41 | baseUrl: '{ the base url of the resource owner\'s site. }', 42 | loginUrl: '{ the login url of the resource owner\'s site. }' 43 | }); 44 | ``` 45 | 46 | ### Making requests to the target meteor app. 47 | Because each meteor application will define their apis in a different way, 48 | this example is pretty generic. It gives you a general idea how things work. 49 | ``` 50 | HTTP.get( 51 | 'http://meteorserver/custom/url', 52 | { 53 | param: { 54 | access_token: '' 55 | } 56 | } 57 | ); 58 | ``` 59 | 60 | ### Examples 61 | There is an example meteor application that demonstrates authentication from 62 | another meteor application. 63 | https://github.com/prime-8-consulting/meteor-oauth2/tree/master/examples 64 | 65 | You will need to run both the resourceServer (the site that holds the usernames and passwords) 66 | and the clientApplication (the site that performs the oauth request). There is 67 | detailed instructions on how to start both applications. They are already 68 | configured to work together, so you should have no problem getting things going. 69 | -------------------------------------------------------------------------------- /packages/meteor-oauth2-client/meteor_server.js: -------------------------------------------------------------------------------- 1 | var OAuth = Package.oauth.OAuth; 2 | var Random = Package.random.Random; 3 | 4 | OAuth.registerService(MeteorOAuth2.serviceName, 2, null, function(query) { 5 | console.log('query', query); 6 | var config = ServiceConfiguration.configurations.findOne({ 7 | service: MeteorOAuth2.serviceName 8 | }); 9 | 10 | if (!config) { 11 | throw new ServiceConfiguration.ConfigError("Service not configured"); 12 | } 13 | 14 | if (!config.baseUrl) { 15 | throw new ServiceConfiguration.ConfigError("Service found but it does not have a baseUrl configured."); 16 | } 17 | 18 | if (!config.loginUrl) { 19 | throw new ServiceConfiguration.ConfigError("Service found but it does not have a loginUrl configured."); 20 | } 21 | 22 | var response = getTokenResponse(query, config); 23 | var accessToken = response.accessToken; 24 | var identity = getIdentity(accessToken, config); 25 | 26 | var serviceData = { 27 | id: identity.id, 28 | accessToken: accessToken, 29 | expiresAt: (+new Date) + (1000 * response.expiresIn), 30 | identity: identity 31 | }; 32 | 33 | return { 34 | serviceData: serviceData, 35 | options: { 36 | profile: { 37 | name: identity.email 38 | } 39 | } 40 | }; 41 | }); 42 | 43 | var isJSON = function(str) { 44 | try { 45 | JSON.parse(str); 46 | return true; 47 | } catch (e) { 48 | return false; 49 | } 50 | }; 51 | 52 | var getTokenResponse = function(query, config) { 53 | var responseContent; 54 | try { 55 | responseContent = HTTP.post( 56 | config.baseUrl + '/oauth/token', 57 | { 58 | params: { 59 | grant_type: 'authorization_code', 60 | code: query.code, 61 | client_id: config.clientId, 62 | client_secret: OAuth.openSecret(config.secret), 63 | redirect_uri: OAuth._redirectUri(MeteorOAuth2.serviceName, config) 64 | } 65 | } 66 | ).content; 67 | } catch (err) { 68 | throw new Error("Failed to complete OAuth handshake\n\n" + err.message); 69 | } 70 | 71 | if (!isJSON(responseContent)) { 72 | throw new Error("Failed to complete OAuth handshake" + responseContent); 73 | } 74 | 75 | var parsedResponse = JSON.parse(responseContent); 76 | var accessToken = parsedResponse.access_token; 77 | var expiresIn = parsedResponse.expires_in; 78 | 79 | if (!accessToken) { 80 | throw new Error("Failed to complete OAuth handshake\n\ 81 | did not receive an oauth token.\n" + responseContent 82 | ); 83 | } 84 | 85 | return { 86 | accessToken: accessToken, 87 | expiresIn: expiresIn 88 | }; 89 | }; 90 | 91 | var getIdentity = function(accessToken, config) { 92 | var fetchUrl = config.baseUrl + '/oauth/getIdentity'; 93 | try { 94 | return HTTP.get( 95 | fetchUrl, 96 | { 97 | params: { 98 | access_token: accessToken 99 | } 100 | } 101 | ).data; 102 | } catch (err) { 103 | throw new Error('Failed to fetch identity from '+ fetchUrl +'. ' + err.message); 104 | } 105 | }; 106 | 107 | MeteorOAuth2.retrieveCredential = function(credentialToken, credentialSecret) { 108 | return OAuth.retrieveCredential(credentialToken, credentialSecret); 109 | }; -------------------------------------------------------------------------------- /examples/resourceServer/client/index.js: -------------------------------------------------------------------------------- 1 | // reactive vars for our UI. 2 | var grantResult = new ReactiveVar(null); 3 | var tokenResult = new ReactiveVar(null); 4 | var getUserIdResult = new ReactiveVar(null); 5 | var clientCount = new ReactiveVar(null); 6 | 7 | Template.authorize.onCreated(function() { 8 | // subscribe to our authorization codes and refresh tokens. 9 | oAuth2Server.subscribeTo.authCode(); 10 | oAuth2Server.subscribeTo.refreshTokens(); 11 | 12 | // get teh client count. 13 | Meteor.call('clientCount', function(err, cnt) { 14 | clientCount.set(cnt); 15 | }); 16 | }); 17 | 18 | Template.authorize.helpers({ 19 | urlParams: function() { 20 | return getUrlParams(); 21 | }, 22 | isUrlParamsValid: function() { 23 | var params = getUrlParams(); 24 | return !!params.client_id && !!params.redirect_uri && !!params.response_type; 25 | }, 26 | grantResult: function() { 27 | return grantResult.get(); 28 | }, 29 | tokenResult: function() { 30 | return tokenResult.get(); 31 | }, 32 | getUserIdResult: function() { 33 | return getUserIdResult.get(); 34 | }, 35 | authCodes: function() { 36 | return oAuth2Server.collections.authCode.find({}); 37 | }, 38 | refreshTokens: function() { 39 | return oAuth2Server.collections.refreshToken.find({}); 40 | }, 41 | clientCount: function() { 42 | return clientCount.get(); 43 | } 44 | }); 45 | 46 | Template.authorize.events({ 47 | /** 48 | * CONFIG FLOW - Step C1.1 49 | * Create an authorized client. 50 | */ 51 | 'submit #addClientForm': function (){ 52 | var newClient = { 53 | active: true, 54 | clientId: $('#addClientForm input[name="clientId"]').val(), 55 | redirectUri: $('#addClientForm input[name="redirectUri"]').val(), 56 | clientSecret: $('#addClientForm input[name="clientSecret"]').val(), 57 | clientName: $('#addClientForm input[name="clientName"]').val() 58 | }; 59 | 60 | Meteor.call('addClient', newClient, function() { 61 | Meteor.call('clientCount', function(err, cnt) { 62 | clientCount.set(cnt); 63 | }); 64 | }); 65 | 66 | return false; 67 | }, 68 | 69 | /** 70 | * AUTH FLOW - Step A5.1 71 | * user clicks the authorize button. 72 | */ 73 | 'click button.authorize': function() { 74 | console.log('Authorize button clicked.'); 75 | var urlParams = getUrlParams(); 76 | 77 | // create an authorization code for the provided client. 78 | oAuth2Server.callMethod.authCodeGrant( 79 | urlParams.client_id, 80 | urlParams.redirect_uri, 81 | urlParams.response_type, 82 | urlParams.scope && urlParams.scope.split(' '), 83 | urlParams.state, 84 | function(err, result) { 85 | console.log(err, result); 86 | 87 | // give the UI something to display. 88 | grantResult.set(result); 89 | } 90 | ); 91 | }, 92 | 93 | /** 94 | * This entire code block is for testing purposes only. The functionality here is generally 95 | * implemented on the client application and this action tends to be server-to-server. 96 | */ 97 | 'click button.testLocalTokens': function() { 98 | var result = grantResult.get(); 99 | var urlParams = getUrlParams(); 100 | 101 | /** 102 | * AUTH FLOW - Step A5 103 | * We have an authorization code. Now get a token. 104 | */ 105 | if (result.success) { 106 | console.log('POST'); 107 | HTTP.post( 108 | Meteor.absoluteUrl('/oauth/token'), 109 | { 110 | headers: { 111 | 'Content-type': 'application/x-www-form-urlencoded' 112 | }, 113 | params: { 114 | grant_type: 'authorization_code', 115 | client_id: urlParams.client_id, 116 | client_secret: '12345', 117 | code: result.authorizationCode 118 | } 119 | }, 120 | function(err, result) { 121 | /** 122 | * AUTH FLOW - Step A6 123 | */ 124 | tokenResult.set(result.data); 125 | 126 | /** 127 | * AUTH FLOW - Step A7. 128 | * we have an access token. Get the user from the REST service. This service is defined 129 | * in the /server/rest.js. 130 | */ 131 | HTTP.get( 132 | Meteor.absoluteUrl('/api/getUserId'), 133 | { 134 | params: { 135 | access_token: result.data.access_token 136 | } 137 | }, 138 | function(err, result) { 139 | // set the userId. 140 | getUserIdResult.set(result.data); 141 | } 142 | ); 143 | } // function 144 | ); // HTTP.post 145 | } // if 146 | }, // function 147 | 148 | 'click button.deleteAllClients': function() { 149 | Meteor.call('deleteAllClients', function() { 150 | Meteor.call('clientCount', function(err, cnt) { 151 | clientCount.set(cnt); 152 | }); 153 | }); 154 | } 155 | }); 156 | 157 | Template.authCodeItem.events({ 158 | 'click button.remove': function() { 159 | oAuth2Server.collections.authCode.remove(this._id); 160 | } 161 | }); 162 | 163 | Template.refreshTokenItem.events({ 164 | 'click button.remove': function() { 165 | oAuth2Server.collections.refreshToken.remove(this._id); 166 | } 167 | }); 168 | 169 | function getUrlParams() { 170 | var match, 171 | pl = /\+/g, // Regex for replacing addition symbol with a space 172 | search = /([^&=]+)=?([^&]*)/g, 173 | decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); }, 174 | query = window.location.search.substring(1); 175 | 176 | var urlParams = {}; 177 | while (match = search.exec(query)) { 178 | urlParams[decode(match[1])] = decode(match[2]); 179 | } 180 | 181 | return urlParams; 182 | } 183 | -------------------------------------------------------------------------------- /packages/meteor-oauth2-server/server.js: -------------------------------------------------------------------------------- 1 | // get the node modules. 2 | var express = Npm.require('express'), 3 | bodyParser = Npm.require('body-parser'), 4 | oauthserver = Npm.require('oauth2-server'); 5 | 6 | 7 | // configure the server-side collections. The rest of the collections 8 | // exist in common.js and are for both client and server. 9 | var accessTokenCollection = new Meteor.Collection('OAuth2AccessTokens'); 10 | var clientsCollection = new Meteor.Collection('OAuth2Clients'); 11 | 12 | 13 | // setup the node oauth2 model. 14 | var meteorModel = new MeteorModel( 15 | accessTokenCollection, 16 | refreshTokensCollection, 17 | clientsCollection, 18 | authCodesCollection, 19 | true 20 | ); 21 | 22 | 23 | // setup the exported object. 24 | oAuth2Server.oauthserver = oauthserver({ 25 | model: meteorModel, 26 | grants: ['authorization_code'], 27 | debug: true 28 | }); 29 | 30 | oAuth2Server.collections.accessToken = accessTokenCollection; 31 | oAuth2Server.collections.client = clientsCollection; 32 | 33 | // configure a url handler for the /oauth/token path. 34 | var app = express(); 35 | 36 | app.use(bodyParser.urlencoded({ extended: true })); 37 | app.use(bodyParser.json()); 38 | app.all('/oauth/token', oAuth2Server.oauthserver.grant()); 39 | 40 | WebApp.rawConnectHandlers.use(app); 41 | 42 | 43 | ///////////////////// 44 | // Configure really basic identity service 45 | //////////////////// 46 | JsonRoutes.Middleware.use( 47 | '/oauth/getIdentity', 48 | oAuth2Server.oauthserver.authorise() 49 | ); 50 | 51 | JsonRoutes.add('get', '/oauth/getIdentity', function(req, res, next) { 52 | console.log('GET /oauth/getIdentity'); 53 | 54 | var accessTokenStr = (req.params && req.params.access_token) || (req.query && req.query.access_token); 55 | var accessToken = oAuth2Server.collections.accessToken.findOne({accessToken: accessTokenStr}); 56 | var user = Meteor.users.findOne(accessToken.userId); 57 | 58 | JsonRoutes.sendResult( 59 | res, 60 | { 61 | data: { 62 | id: user._id, 63 | email: user.emails[0].address 64 | } 65 | } 66 | ); 67 | }); 68 | 69 | 70 | 71 | //////////////////// 72 | // Meteor publish. 73 | /////////////////// 74 | Meteor.publish(oAuth2Server.pubSubNames.authCodes, function() { 75 | if (!this.userId) { 76 | return this.ready(); 77 | } 78 | 79 | return oAuth2Server.collections.authCode.find({ 80 | userId: this.userId, 81 | expires: { 82 | $gt: new Date() 83 | } 84 | }); 85 | }); 86 | 87 | Meteor.publish(oAuth2Server.pubSubNames.refreshTokens, function() { 88 | if (!this.userId) { 89 | return this.ready(); 90 | } 91 | 92 | return oAuth2Server.collections.refreshToken.find({ 93 | userId: this.userId, 94 | expires: { 95 | $gt: new Date() 96 | } 97 | }); 98 | }); 99 | 100 | //////////// 101 | // configure the meteor methods. 102 | ////////////// 103 | var methods = {}; 104 | methods[oAuth2Server.methodNames.authCodeGrant] = function(clientId, redirectUri, responseType, scope, state) { 105 | // validate parameters. 106 | check(clientId, String); 107 | check(redirectUri, String); 108 | check(responseType, String); 109 | check(scope, Match.Optional(Match.OneOf(null, [String]))); 110 | check(state, Match.Optional(Match.OneOf(null, String))); 111 | 112 | if (!scope) { 113 | scope = []; 114 | } 115 | 116 | // validate the user is authenticated. 117 | var userId = this.userId; 118 | if (!userId) { 119 | return { 120 | success: false, 121 | error: 'User not authenticated.' 122 | }; 123 | } 124 | 125 | // The oauth2-server project relies heavily on express to validate and 126 | // manipulate the oauth2 grant. A forthcoming version will abstract this 127 | // behaviour into promises. 128 | // That being the case, we need to get run an authorization grant as if 129 | // it were a promise. Warning, the following code is difficult to follow. 130 | // What we are doing is mocking and express app but never attaching it to 131 | // Meteor. This allows oauth2-server to behave as it would as if it was 132 | // natively attached to the webapp. The following code mocks express, 133 | // request, response, check and next in order to retrive the data we need. 134 | // Further, we are also running this in a synchronous manner. Enjoy! :) 135 | 136 | // create check callback that returns the user. 137 | var checkCallback = function (req, callback) { 138 | callback( 139 | null, // error. 140 | true, // user authorizes the code creation. 141 | { 142 | id: userId 143 | } 144 | ); 145 | }; 146 | 147 | // retrieve the grant function from oauth2-server. This method setups up the 148 | // this context and such. The returned method is what express would normally 149 | // expect when handling a URL. eg. function(req, res, next) 150 | var authCodeGrantFn = oAuth2Server.oauthserver.authCodeGrant(checkCallback); 151 | 152 | // make the grant function run synchronously. 153 | var authCodeGrantFnSync = Async.wrap(function (done) { 154 | // the return object. 155 | var response = { 156 | success: false, 157 | error: null, 158 | authorizationCode: null, 159 | redirectToUri: null 160 | }; 161 | 162 | // create mock express app. 163 | var mockApp = express(); 164 | var req = mockApp.request; 165 | 166 | // set the request body values. In a typical express setup, the body 167 | // would be parsed by the body-parser package. We are cutting out 168 | // the middle man, so to speak. 169 | req.body = { 170 | client_id: clientId, 171 | response_type: responseType, 172 | redirect_uri: redirectUri 173 | }; 174 | req.query = {}; 175 | 176 | // listen for redirect calls. 177 | var res = mockApp.response; 178 | res.redirect = function (uri) { 179 | response.redirectToUri = uri; 180 | 181 | // we have what we need, trigger the done function with the response data. 182 | done(null, response); 183 | }; 184 | 185 | // listen for errors. 186 | var next = function (err) { 187 | response.error = err; 188 | 189 | // we have what we need, trigger the done function with the response data. 190 | done(null, response); 191 | }; 192 | 193 | // call the async function with the mocked params. 194 | authCodeGrantFn(req, res, next); 195 | }); 196 | 197 | // run the auth code grant function in a synchronous manner. 198 | var result = authCodeGrantFnSync(); 199 | 200 | 201 | // update the success flag. 202 | result.success = !result.error && !(/[?&]error=/g).test(result.redirectToUri); 203 | 204 | // set the authorization code. 205 | if (result.redirectToUri) { 206 | var match = result.redirectToUri.match(/[?&]code=([0-9a-f]+)/); 207 | if (match.length > 1) { 208 | result.authorizationCode = match[1]; 209 | } 210 | 211 | // add the state to the url. 212 | if (state) { 213 | result.redirectToUri += '&state=' + state; 214 | } 215 | } 216 | //console.log(result); 217 | 218 | return result; 219 | }; 220 | 221 | Meteor.methods(methods); 222 | -------------------------------------------------------------------------------- /packages/meteor-oauth2-server/meteor-model.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A oauth2-server model for dealing with the meteor collections. Original code 3 | * from: https://github.com/RocketChat/rocketchat-oauth2-server/blob/master/model.coffee 4 | * Modifications and improvements have been made. 5 | * This class is used a callback model for oauth2-server. oauth2-server runs it's calls 6 | * in a different context and fiber. Doing so can get really messy when attempting to 7 | * run Meteor code, like Collection calls. We work-around this problem by creating 8 | * instance methods are runtime that are proxied through Meteor.bindEnvironment. 9 | * This strategy allows us to specify the this context. 10 | * Defining the class with prototype methods defined by Meteor.bindEnvironment 11 | * would ensure we lose our this context when the method executes. 12 | */ 13 | MeteorModel = (function() { 14 | function MeteorModel(accessTokenCollection, 15 | refreshTokenCollection, 16 | clientCollection, 17 | authCodeCollection, 18 | debug) { 19 | this.accessTokenCollection = accessTokenCollection; 20 | this.refreshTokenCollection = refreshTokenCollection; 21 | this.clientCollection = clientCollection; 22 | this.authCodeCollection = authCodeCollection; 23 | this.debug = debug; 24 | 25 | /////////////////// 26 | // Defining the methods. 27 | /////////////////// 28 | 29 | this.getAccessToken = Meteor.bindEnvironment( 30 | function (bearerToken, callback) { 31 | if (this.debug === true) { 32 | console.log('[OAuth2Server]', 'in getAccessToken (bearerToken:', bearerToken, ')'); 33 | } 34 | 35 | try { 36 | var token = this.accessTokenCollection.findOne({ 37 | accessToken: bearerToken 38 | }); 39 | 40 | callback(null, token); 41 | 42 | } catch (e) { 43 | callback(e); 44 | } 45 | }, 46 | null, // exception handler 47 | this // this context. 48 | ); 49 | 50 | this.getClient = Meteor.bindEnvironment( 51 | function (clientId, clientSecret, callback) { 52 | if (this.debug === true) { 53 | console.log('[OAuth2Server]', 'in getClient (clientId:', clientId, ', clientSecret:', clientSecret, ')'); 54 | } 55 | 56 | try { 57 | var client; 58 | if (clientSecret == null) { 59 | client = this.clientCollection.findOne({ 60 | active: true, 61 | clientId: clientId 62 | }); 63 | } else { 64 | client = this.clientCollection.findOne({ 65 | active: true, 66 | clientId: clientId, 67 | clientSecret: clientSecret 68 | }); 69 | } 70 | 71 | callback(null, client); 72 | 73 | } catch (e) { 74 | callback(e); 75 | } 76 | }, 77 | null, // exception handler 78 | this // this context. 79 | ); 80 | 81 | 82 | this.grantTypeAllowed = Meteor.bindEnvironment( 83 | function (clientId, grantType, callback) { 84 | if (this.debug === true) { 85 | console.log('[OAuth2Server]', 'in grantTypeAllowed (clientId:', clientId, ', grantType:', grantType + ')'); 86 | } 87 | 88 | callback(false, grantType === 'authorization_code'); 89 | }, 90 | null, // exception handler 91 | this // this context. 92 | ); 93 | 94 | this.saveAccessToken = Meteor.bindEnvironment( 95 | function (token, clientId, expires, user, callback) { 96 | if (this.debug === true) { 97 | console.log('[OAuth2Server]', 'in saveAccessToken (token:', token, ', clientId:', clientId, ', user:', user, ', expires:', expires, ')'); 98 | } 99 | 100 | try { 101 | var tokenId = this.accessTokenCollection.insert({ 102 | accessToken: token, 103 | clientId: clientId, 104 | userId: user.id, 105 | expires: expires 106 | }); 107 | 108 | callback(null, tokenId); 109 | 110 | } catch (e) { 111 | callback(e); 112 | } 113 | }, 114 | null, // exception handler 115 | this // this context. 116 | ); 117 | 118 | this.getAuthCode = Meteor.bindEnvironment( 119 | function (authCode, callback) { 120 | if (this.debug === true) { 121 | console.log('[OAuth2Server]', 'in getAuthCode (authCode: ' + authCode + ')'); 122 | } 123 | 124 | try { 125 | var code = this.authCodeCollection.findOne({ 126 | authCode: authCode 127 | }); 128 | 129 | callback(null, code); 130 | 131 | } catch (e) { 132 | callback(e); 133 | } 134 | }, 135 | null, // exception handler 136 | this // this context. 137 | ); 138 | 139 | this.saveAuthCode = Meteor.bindEnvironment( 140 | function (code, clientId, expires, user, callback) { 141 | Meteor.bindEnvironment(code, clientId, expires, user, callback) 142 | if (this.debug === true) { 143 | console.log('[OAuth2Server]', 'in saveAuthCode (code:', code, ', clientId:', clientId, ', expires:', expires, ', user:', user, ')'); 144 | } 145 | 146 | try { 147 | this.authCodeCollection.remove({authCode: code}); 148 | var codeId = this.authCodeCollection.insert({ 149 | authCode: code, 150 | clientId: clientId, 151 | userId: user.id, 152 | expires: expires 153 | }); 154 | 155 | callback(null, codeId); 156 | 157 | } catch (e) { 158 | callback(e); 159 | } 160 | }, 161 | null, // exception handler 162 | this // this context. 163 | ); 164 | 165 | this.saveRefreshToken = Meteor.bindEnvironment( 166 | function (token, clientId, expires, user, callback) { 167 | if (this.debug === true) { 168 | console.log('[OAuth2Server]', 'in saveRefreshToken (token:', token, ', clientId:', clientId, ', user:', user, ', expires:', expires, ')'); 169 | } 170 | 171 | try { 172 | this.refreshTokenCollection.remove({refreshToken: token}); 173 | var tokenId = this.refreshTokenCollection.insert({ 174 | refreshToken: token, 175 | clientId: clientId, 176 | userId: user.id, 177 | expires: expires 178 | }); 179 | 180 | callback(null, tokenId); 181 | 182 | } catch (e) { 183 | callback(e); 184 | } 185 | }, 186 | null, // exception handler 187 | this // this context. 188 | ); 189 | 190 | this.getRefreshToken = Meteor.bindEnvironment( 191 | function (refreshToken, callback) { 192 | if (this.debug === true) { 193 | console.log('[OAuth2Server]', 'in getRefreshToken (refreshToken: ' + refreshToken + ')'); 194 | } 195 | 196 | try { 197 | var token = this.refreshTokenCollection.findOne({ 198 | refreshToken: refreshToken 199 | }); 200 | 201 | callback(null, token); 202 | 203 | } catch (e) { 204 | callback(e); 205 | } 206 | }, 207 | null, // exception handler 208 | this // this context. 209 | ); 210 | }; 211 | 212 | return MeteorModel; 213 | })(); 214 | -------------------------------------------------------------------------------- /examples/resourceServer/client/index.html: -------------------------------------------------------------------------------- 1 | 2 | ResourceServer 3 | 4 | 5 | 6 |

Welcome to ResourceServer!

7 | 8 | {{> authorize}} 9 | 10 | 11 | 229 | 230 | 233 | 234 | 237 | --------------------------------------------------------------------------------