Skip to content

Commit

Permalink
Merge pull request #2 from Brightspace/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
davewagler-d2l authored Sep 22, 2017
2 parents 8feaee8 + 45a2e95 commit 239767e
Show file tree
Hide file tree
Showing 19 changed files with 343 additions and 242 deletions.
20 changes: 20 additions & 0 deletions .jshintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"node": true,
"bitwise": true,
"camelcase": false,
"curly": true,
"eqeqeq": true,
"immed": true,
"newcap": true,
"noarg": true,
"quotmark": "single",
"regexp": true,
"undef": true,
"unused": false,
"strict": true,
"trailing": true,
"smarttabs": false,
"laxcomma": true,
"onevar": false,
"esversion": 6
}
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Out of the box this solution was built to work with the Devcop Brightspace insta
```
5. Now that the required Node packages are installed the local node server can be started by running:
```shell
npm run start
npm run local
```
6. The server should now be up and running locally and in a browser you can navigate to:
https://localhost:3434
Expand Down
42 changes: 21 additions & 21 deletions docs/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,25 @@ The code for the ID/Key Authentication can be found in the [idkeyauth.js](../src
```
* Once the SDK is imported we can create application context using the Instance URL, the Application Key and the Application Id. The Application Key and Application Id are received when an application is registered in Brightspace using the 'Manage Extensibility' tool. The code for creating this context is:
```javascript
var appContext = new d2l.ApplicationContext(configs.instanceUrl, configs.applicationId, configs.applicationKey);
const appContext = new d2l.ApplicationContext(configs.instanceUrl, configs.applicationId, configs.applicationKey);
```
* The ```/idkeyauth``` route exists in the project to initiate the ID/Key Authentication protocol using the created application context. When this route is navigated to in the browser the user is redirected to the Learning Environment where they are pompted to accept the application's ability to make APIs on their behalf. You can see in the callback that we call the [```createUserContext```](https://github.com/Brightspace/valence-sdk-javascript/blob/master/lib/valence.js#L266) which grabs userId and userKey from the query parameters returned from Brightspace.
* Once they have accepted the terms the user is redirected to the ```/idkeycallback``` route where the received userKey and userId are stored in a cookie so that subsequent requests can be signed using this context. The follwing code is how the context is setup again and used:
```javascript
// Grab the UserId and UserKey from the cookie.
var userId = req.cookies[configs.cookieName].userId;
var userKey = req.cookies[configs.cookieName].userKey;
const userId = req.cookies[configs.cookieName].userId;
const userKey = req.cookies[configs.cookieName].userKey;
// Setup user context using the values from the cookie.
var userContext = appContext.createUserContextWithValues(configs.instanceScheme + '//' + configs.instanceUrl, configs.instancePort, userId, userKey);
const userContext = appContext.createUserContextWithValues(configs.instanceScheme + '//' + configs.instanceUrl, configs.instancePort, userId, userKey);

// Create an authenticated URL using the SDK.
var apiCallUrl = userContext.createAuthenticatedUrl(apiPath, 'GET');
const apiCallUrl = userContext.createAuthenticatedUrl(apiPath, 'GET');
```

## OAuth 2.0
The code for the OAuth 2.0 implementation can be found in the [oauth.js](../src/authorization/oauth.js) file. Out of the box there are many supported OAuth 2.0 libraries that you can use in order to make your authenticated requests and support you through the authentication workflow. One thing to keep in mind is that OAuth 2.0 requires the calling application to be granted ```scopes``` that represent what routes the OAuth client can execute.
The code for the OAuth 2.0 implementation can be found in the [oauth.js](../src/authorization/oauth.js) file. Out of the box there are many supported OAuth 2.0 libraries that you can use in order to make your authenticated requests and support you through the authentication workflow. One thing to keep in mind is that OAuth 2.0 requires the calling application to be granted ```scopes``` that represent what routes the OAuth client is authorized to access.

Currently for the samples the following scopes:
* ```core:*:*```
Expand All @@ -41,9 +41,9 @@ The following is the workflow the sample has implemented:
* The first order of business is to attain an authorization code from the [Authorization Endpoint](http://docs.valence.desire2learn.com/basic/oauth2.html#setting-up-oauth-2-0-authentication). In order to recieve an auth code there are several configurations that need to be sent as query parameters. The following code illustrates this:
```javascript
// Using the imported 'querystring' library, create the query parameter list passing in the required variables.
var authCodeParams = querystring.stringify({
response_type: "code",
redirect_uri: configs.getRedirectUri(req),
const authCodeParams = querystring.stringify({
response_type: 'code',
redirect_uri: helpers.getRedirectUri(req),
client_id: configs.clientId,
scope: configs.authCodeScope,
state: configs.state
Expand All @@ -55,24 +55,24 @@ The following is the workflow the sample has implemented:
* Once the user has granted the application permission the user is redirected back to the ```/oauthcallback``` route. In the callback the recieved Authorization Code is exchanged for an Access Token by calling the [Token Endpoint](http://docs.valence.desire2learn.com/basic/oauth2.html#setting-up-oauth-2-0-authentication) that can then be used to make API calls. The following code is responsible for this exchange:
```javascript
// Retrieve the authorization code from the query parameter.
var authorizationCode = req.query.code;
const authorizationCode = req.query.code;
// Verify that the state passed into the request for an Auth code matches the state passed back to the callback.
var state = req.query.state;
const state = req.query.state;
if (state !== configs.state) {
console.log("The state value from the authorization request was incorrect.");
res.status(500).send({ error: "STATE mistmatch - authorization request could not be completed." });
console.log('The state value from the authorization request was incorrect.');
res.status(500).send({ error: 'STATE mistmatch - authorization request could not be completed.' });
return;
}
// Set the values that will be sent to the Token Endpoint through the body of the request.
var payload = querystring.stringify({
grant_type: "authorization_code",
const payload = querystring.stringify({
grant_type: 'authorization_code',
redirect_uri: configs.getRedirectUri(req),
code: authorizationCode
});
// Using the 'superagent' library with the clientId and ClientSecret sent through the headers as Basic Authorization and the payload sent as the body.
// Using the 'superagent' library with the client_id and client_secret sent through the headers as Basic Authorization and the payload sent as the body.
request
.post(configs.tokenEndpoint)
.auth(configs.clientId, configs.clientSecret)
Expand All @@ -81,10 +81,10 @@ The following is the workflow the sample has implemented:
if (err) {
console.log('Access Token Error', err.response || err);
res.redirect('/auth');
} else if(response.statusCode != 200) {
} else if(response.statusCode !== 200) {
res.status(response.statusCode).send(response.error);
} else {
var accessToken = response.body.access_token;
const accessToken = response.body.access_token;
// Save the access token into a cookie to be retrieved later in order to make a request.
res.cookie(configs.cookieName, { accessToken: accessToken }, configs.cookieOptions);
// Redirect the user back to the index page.
Expand All @@ -95,17 +95,17 @@ The following is the workflow the sample has implemented:
* Now that the Access Token has been saved in the cookie, it can be retrieved later and added as an 'Authorization' header in API requests. The following is an example of this:
```javascript
// Retrieve access token from the cookie.
var accessToken = req.cookies[configs.cookieName].accessToken;
const accessToken = req.cookies[configs.cookieName].accessToken;
// Set the Authorization header with the access token.
request
.get( whoamiRoute )
.set('Authorization', `Bearer ${accessToken}`)
.end(function(error, response) {
if (error) {
console.log("Error calling the who am I route", error);
console.log('Error calling the who am I route', error);
res.status(500).send({ error: error });
} else if(response.statusCode != 200) {
} else if(response.statusCode !== 200) {
res.status(response.statusCode).send(response.error);
} else {
res.status(200).send(response.text);
Expand Down
2 changes: 1 addition & 1 deletion docs/configurations.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ The [index.html]() page has several hardcoded values that indicate to the underl
* ```moduleId``` can be updated to the module in content where you would like the new file to be added.
* Note: if you are changing the values for the content route be sure to checkout the [content.js](../src/content.js) file in order to update the topic data block to point to the proper content location ('Url' field):
```javascript
var topicData = {
const topicData = {
Title: "Sample Word Document Content",
ShortTitle: null,
Type: 1,
Expand Down
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
"description": "Sample application showing how to extend Brightspace using APIs, Remote Plugins and other integration points..",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node server-local.js"
"lint": "jshint src server-local.js server.js",
"local": "node server-local.js",
"start": "node server.js",
"test": "npm run lint"
},
"author": "Desire2Learn Inc.",
"license": "Apache License 2.0",
Expand All @@ -19,6 +21,7 @@
"valence": "^1.0.3"
},
"devDependencies": {
"jshint": "^2.9.5",
"openssl-self-signed-certificate": "^1.1.6"
}
}
11 changes: 6 additions & 5 deletions server-local.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
var https = require('https');
var selfSigned = require('openssl-self-signed-certificate');
'use strict';

var app = require('./server');
const https = require('https'),
selfSigned = require('openssl-self-signed-certificate'),
app = require('./server');

var httpsPort = process.env.HTTPS_PORT || 3434;
var options = {
const httpsPort = process.env.HTTPS_PORT || 3434;
const options = {
key: selfSigned.key,
cert: selfSigned.cert
};
Expand Down
27 changes: 14 additions & 13 deletions server.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
var
'use strict';

const
d2l = require('valence'),
express = require('express'),
request = require('superagent'),
bodyParser = require('body-parser'),
cookieParser = require('cookie-parser'),
configs = require('./src/configurations'),
Expand All @@ -12,23 +13,23 @@ app.use(bodyParser.urlencoded({ extended: true }));
app.use(cookieParser());

// Setup the initial D2L context object using the configured instance settings.
var appContext = new d2l.ApplicationContext(configs.instanceUrl, configs.applicationId, configs.applicationKey);
const appContext = new d2l.ApplicationContext(configs.instanceUrl, configs.applicationId, configs.applicationKey);

// Import Authorization
require('./src/authorization/idkeyauth.js')(app, configs, appContext);
require('./src/authorization/oauth.js')(app, request, configs);
app.use(require('./src/authorization/idkeyauth.js')(appContext));
app.use(require('./src/authorization/oauth.js')());

// Import Sample API Calls
require('./src/apis/whoami')(app, request, configs, appContext);
require('./src/apis/content')(app, request, configs, appContext);
require('./src/apis/grades')(app, request, configs, appContext);
require('./src/apis/profileimage')(app, request, configs, appContext, __dirname);
app.use(require('./src/apis/whoami')(appContext));
app.use(require('./src/apis/content')(appContext));
app.use(require('./src/apis/grades')(appContext));
app.use(require('./src/apis/profileimage')(appContext, __dirname));

// Import Sample Remote Plugins
require('./src/remote-plugins/isf-cim')(app, request, configs, appContext, path, __dirname);
require('./src/remote-plugins/quicklink-cim')(app, request, configs, appContext, path, __dirname);
require('./src/remote-plugins/courseimport-cim')(app, request, configs, appContext, path, __dirname);
require('./src/remote-plugins/statics.js')(app, express, __dirname);
app.use(require('./src/remote-plugins/isf-cim')(appContext, __dirname));
app.use(require('./src/remote-plugins/quicklink-cim')(appContext,__dirname));
app.use(require('./src/remote-plugins/courseimport-cim')(appContext, __dirname));
require('./src/remote-plugins/statics.js')(app, __dirname);

/* GET /
* The default server location that will return the index html page.
Expand Down
Loading

0 comments on commit 239767e

Please sign in to comment.