Skip to content

Commit

Permalink
Convert repo to V2 sample strategy (#31)
Browse files Browse the repository at this point in the history
Restructuring this repository in the new scheme.  Tests will come as a separate PR and will be handled by the TCK
  • Loading branch information
robertjd authored Jan 6, 2018
1 parent b14a32f commit 5d7adfe
Show file tree
Hide file tree
Showing 79 changed files with 2,692 additions and 7,360 deletions.
81 changes: 81 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
{
"rules": {
"curly": [
2
],
"eqeqeq": [
2
],
"eol-last": ["error", "always"],
"indent": [
2,
2,
{
"SwitchCase": 1
}
],
"linebreak-style": [
2,
"unix"
],
"new-cap": [
2,
{
"properties": false
}
],
"no-unused-vars": [
2,
{
"vars": "all",
"args": "after-used"
}
],
"no-use-before-define": [
2
],
"no-trailing-spaces": [
2
],
"quotes": [
2,
"single"
],
"semi": [
2,
"always"
],
"wrap-iife": [
2,
"outside"
],
"brace-style": 2,
"block-spacing": [
2,
"always"
],
"keyword-spacing": [2, {"before": true, "after": true, "overrides": {}}],
"space-before-blocks": 2,
"space-before-function-paren": [2, {"anonymous": "always", "named": "never"}],
"comma-spacing": [2, {"before": false, "after": true}],
"comma-style": [2, "last"],
"no-lonely-if": 2,
"array-bracket-spacing": [2, "never"],
"no-spaced-func": [2],
"space-in-parens": [2, "never"],
"space-infix-ops": 2
},
"globals": {
"after": true,
"afterEach": true,
"before": true,
"beforeEach": true,
"describe": true,
"it": true
},
"env": {
"es6": true,
"node": true
},
"extends": "eslint:recommended"
}
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ build2
node_modules
dist
npm-debug.log
travis_phantomjs
.DS_Store
.vscode
.samples.config.json
23 changes: 0 additions & 23 deletions .samples.config.json

This file was deleted.

10 changes: 0 additions & 10 deletions .yo-rc.json

This file was deleted.

175 changes: 8 additions & 167 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,170 +1,11 @@
# AngularJS 1.x and express-4 Sample Application
# Express Sample Applications for Okta

> Note: This version of Express back-end ships with our [Angular 1 front-end sample](https://github.com/okta/samples-js-angular-1). It is not not compatible with the [React front-end sample](https://www.npmjs.com/package/@okta/samples-js-react) and [Elm front-end sample](https://www.npmjs.com/package/@okta/samples-elm).
To use this Express sample with the React and Elm front-end samples, please use [previous version](https://github.com/okta/samples-nodejs-express-4/tree/release) and follow the instruction [here](https://github.com/okta/samples-nodejs-express-4/tree/release#using-a-different-front-end)
This repository contains several sample applications that show you how to integrate various Okta use-cases into your Node.js application that uses the Express framework.

### Table of Contents
Please find the sample that fits your use-case from the table below.

- [Introduction](#introduction)
- [Login Redirect](#1-login-redirect)
- [Prerequisites](#prerequisites)
- [Quick Start](#quick-start)
- [Front End](#front-end)
- [Login Redirect](#login-redirect)
- [Using a different front-end](#using-a-different-front-end)
- [Back End](#back-end)
- [Routes](#routes)
- [Handle the Redirect](#handle-the-redirect)
- [Logout](#logout)
- [Conclusion](#conclusion)
- [Support](#support)
- [License](#license)

## Introduction

This tutorial will demonstrate how to use OAuth 2.0 and OpenID Connect to add authentication to a NodeJs/express-4 application.
> Note: This version of Express sample demonstrates the [Authorization Code Flow](https://tools.ietf.org/html/rfc6749#section-1.3.1) via redirect to Okta org URL. Support for Custom Login Form will be added in a future release.

### 1. Login Redirect

Users are redirected to your Okta organization for authentication.

<img src="docs/assets/redirect.png" width="300" />

After logging into your Okta organization, an authorization code is returned in a callback URL. This authorization code is then exchanged for an `id_token`.

## Prerequisites

This sample app depends on [Node.js](https://nodejs.org/en/) for front-end dependencies and some build scripts - if you don't have it, install it from [nodejs.org](https://nodejs.org/en/).

```bash
# Verify that node is installed
$ node -v
```

Then, clone this sample from GitHub and install the front-end dependencies:
```bash
# Clone the repo and navigate to the samples-nodejs-express-4 dir
$ git clone [email protected]:okta/samples-nodejs-express-4.git && cd samples-nodejs-express-4

# Install the front-end dependencies
[samples-nodejs-express-4]$ npm install
```

## Quick Start

Start the back-end for your sample application with `npm start`. This will start the app server on [http://localhost:8080](http://localhost:8080).

By default, this application uses a mock authorization server which responds to API requests like a configured Okta org - it's useful if you haven't yet set up OpenID Connect but would still like to try this sample.

To start the mock server, run the following in a second terminal window:
```bash
# Starts the mock Okta server at http://127.0.0.1:7777
[samples-nodejs-express-4]$ npm run mock-okta
```

You can create your own Okta org by signing up for a [free Developer Account](https://developer.okta.com/signup/)

If you'd like to test this sample against your own Okta org, navigate to the Okta Developer Dashboard and follow these steps:

1. Create a new **Web** application by clicking **Add Application** and selecting **Web** from the *Applications* page.
2. After accepting the default configuration, click **Done** to redirect back to the *General Settings* of your application.
3. Copy the **Client ID** and **Client Secret**, as it will be needed for the client configuration.
4. Finally, navigate to `https://{yourOktaDomain}.com/api/v1/authorizationServers/default` to see if the [Default Authorization Server](https://developer.okta.com/docs/api/resources/oauth2.html#using-the-default-authorization-server) is setup. If not, [let us know](mailto:[email protected]).
Then, replace the *oidc* settings in `.samples.config.json` to point to your new app:
```javascript
// .samples.config.json
{
"oidc": {
"oktaUrl": "https://{{yourOktaDomain}}.com",
"issuer": "https://{{yourOktaDomain}}.com/oauth2/default",
"clientId": "{{yourClientId}}",
"clientSecret": "{{yourClientSecret}}",
"redirectUri": "http://localhost:8080/authorization-code/callback"
}
}
```

## Front-end

When you start this sample, the [AngularJS 1.x UI](https://github.com/okta/samples-js-angular-1) is copied into the `dist/` directory. More information about the AngularJS controllers and views are available in the [AngularJS project repository](https://github.com/okta/samples-js-angular-1/blob/master/README.md).

### Login Redirect

> Note: The middleware library [okta oidc-middleware](https://github.com/okta/okta-oidc-js/tree/master/packages/oidc-middleware) is used by the Express back-end to handle the [Authorization Code Flow](https://tools.ietf.org/html/rfc6749#section-1.3.1)
With AngularJS, we include the template directive `ng-click` to begin the login process. When the link is clicked, it calls the `login()` function which simply redirects the client to `/login` URL.

In this sample, the [okta oidc-middleware](https://github.com/okta/okta-oidc-js/tree/master/packages/oidc-middleware) library is configured to use the values in `.samples.config.json`. When the `login()` function is called from the view, the middleware library calls the [`/authorize`](http://developer.okta.com/docs/api/resources/oauth2.html#authentication-request) endpoint to start the [Authorization Code Flow](https://tools.ietf.org/html/rfc6749#section-1.3.1).

You can read more about the `ExpressOIDC` configuration options here: [OpenID Connect with Okta oidc-middleware](https://github.com/okta/okta-oidc-js/tree/master/packages/oidc-middleware#new-expressoidcconfig).

**Important:** When the authorization code is exchanged for an `access_token` and/or `id_token`, the tokens **must** be [validated](#validation). The middleware library handles this validation on the client's behalf.

## Back-end
To display the user information and handle session close, your back-end server performs the following tasks:
- Handle the callback from [okta oidc-middleware](https://github.com/okta/okta-oidc-js/tree/master/packages/oidc-middleware) after authentication
- Render the user profile information returned by the middleware library
- Log the user out

> Note: As you can see, the back-end has delegated handling the authorization code exchange, token validation, getting the userinfo etc to [okta oidc-middleware](https://github.com/okta/okta-oidc-js/tree/master/packages/oidc-middleware).
In the earlier version of this sample, the back-end performed all the heavy lifting right from token exchange, validation to setting the user session.

### Routes
To render the AngularJS templates, we define the following express-4 routes:

| Route | Description |
| ------------------------------------- | ----------------------------------------------------------- |
| **authorization-code/** | index page, also handles redirect from middleware |
| **authorization-code/login-redirect** | renders the [login redirect](#login-redirect) flow |
| **authorization-code/profile** | renders the logged in state, displaying profile information |
| **authorization-code/logout** | closes the `user` session |

### Handle the Redirect
After successful authentication, we get information about the authenticated user in `req.userinfo` object.
Below is the code that handles the redirect back after successful authentication:

```javascript
// route-handlers.js
handlers.scenarios = (req, res) => {
if (req.isAuthenticated()) {
res.redirect(302, '/authorization-code/profile');
return;
}
res.render('overview', { config });
};

handlers.profile = (req, res) => {
if (!req.isAuthenticated()) {
res.redirect(302, '/');
return;
}
res.render('profile', { user: req.userinfo, config });
};
```
### Logout
We can logout the user from the session using the [okta oidc-middleware](https://github.com/okta/okta-oidc-js/tree/master/packages/oidc-middleware)

```javascript
// route-handlers.js
handlers.logout = (req, res) => {
req.logout();
res.redirect('/');
};
```

## Conclusion
You have now successfully authenticated with Okta! Now what? With the `req.userinfo` object, you have basic claims into the user's identity. You can extend the set of claims by modifying the `response_type` and `scopes` to retrieve custom information about the user. This includes `locale`, `address`, `phone_number`, `groups`, and [more](http://developer.okta.com/docs/api/resources/oidc.html#scopes).

## Support

Have a question or see a bug? Email [email protected]. For feature requests, feel free to open an issue on this repo. If you find a security vulnerability, please follow our [Vulnerability Reporting Process](https://www.okta.com/vulnerability-reporting-policy/).

## License

Copyright 2017 Okta, Inc. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
| Sample | Description | Use-Case |
|--------|-------------|----------|
| [Okta-Hosted Login](/okta-hosted-login) | An application server that uses the hosted login page on your Okta org, then creates a cookie session for the user in the Express application. | Traditional web applications with server-side rendered pages. |
| [Custom Login Page](/custom-login) | An application server that uses the Okta Sign-In Widget on a custom login page within the application, then creates a cookie session for the user in the Express application. | Traditional web applications with server-side rendered pages. |
| [Resource Server](/resource-server) | This is a sample API resource server that shows you how to authenticate requests with access tokens that have been issued by Okta. | Single-Page applications. |
File renamed without changes.
364 changes: 364 additions & 0 deletions common/assets/css/semantic.min.css

Large diffs are not rendered by default.

84 changes: 84 additions & 0 deletions common/sample-web-server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/**
* A simple web server that initializes the OIDC Middleware library with the
* given options, and attaches route handlers for the example profile page
* and logout functionality.
*/

const express = require('express');
const session = require('express-session');
const mustacheExpress = require('mustache-express');
const path = require('path');
const { ExpressOIDC } = require('@okta/oidc-middleware');

const templateDir = path.join(__dirname, '..', 'common', 'views');
const frontendDir = path.join(__dirname, '..', 'common', 'assets');

module.exports = function SampleWebServer(sampleConfig, extraOidcOptions, homePageTemplateName) {

const oidc = new ExpressOIDC(Object.assign({
issuer: sampleConfig.oidc.issuer,
client_id: sampleConfig.oidc.clientId,
client_secret: sampleConfig.oidc.clientSecret,
redirect_uri: sampleConfig.oidc.redirectUri,
scope: sampleConfig.oidc.scope
}, extraOidcOptions || {}));

const app = express();

app.use(session({
secret: 'this-should-be-very-random',
resave: true,
saveUninitialized: false
}));

// Provide the configuration to the view layer because we show it on the homepage
const displayConfig = Object.assign(
{},
sampleConfig.oidc,
{
clientSecret: '****' + sampleConfig.oidc.clientSecret.substr(sampleConfig.oidc.clientSecret.length - 4, 4)
}
);

app.locals.oidcConfig = displayConfig;

// This server uses mustache templates located in views/ and css assets in assets/
app.use('/assets', express.static(frontendDir));
app.engine('mustache', mustacheExpress());
app.set('view engine', 'mustache');
app.set('views', templateDir);

app.use(oidc.router);

app.get('/', (req, res) => {
const template = homePageTemplateName || 'home';
res.render(template, {
isLoggedIn: !!req.userinfo,
userinfo: req.userinfo
});
});

app.get('/profile', oidc.ensureAuthenticated(), (req, res) => {
// Convert the userinfo object into an attribute array, for rendering with mustache
const attributes = Object.entries(req.userinfo);
res.render('profile', {
isLoggedIn: !!req.userinfo,
userinfo: req.userinfo,
attributes
});
});

app.get('/logout', (req, res) => {
req.logout();
res.redirect('/');
});

oidc.on('ready', () => {
app.listen(sampleConfig.port, () => console.log(`App started on port ${sampleConfig.port}`));
});

oidc.on('error', err => {
// An error occurred while setting up OIDC
throw err;
});
};
Loading

0 comments on commit 5d7adfe

Please sign in to comment.