Skip to content
This repository has been archived by the owner on Oct 24, 2024. It is now read-only.

Commit

Permalink
📦 Prepping 1.0.0 react (#171)
Browse files Browse the repository at this point in the history
BREAKING CHANGE
  • Loading branch information
jmelberg-okta authored Mar 28, 2018
1 parent efae2a8 commit 40a82cc
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 52 deletions.
73 changes: 38 additions & 35 deletions packages/okta-react/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,11 +169,14 @@ export default withAuth(class MessageList extends Component {
Security is the top-most component of okta-react. This is where most of the configuration is provided.

#### Configuration options
* **issuer** (required) - The OpenId Connect `issuer`
* **client_id** (required) - The OpenId Connect `client_id`
* **redirect_uri** (required) - Where the callback handler is hosted
* **onAuthRequired** (optional)
* **auth** (optional) - Provide an Auth object instead of the options above. This is helpful when integrating `okta-react` with external libraries that need access to the tokens.

- **issuer** (required) - The OpenId Connect `issuer`
- **client_id** (required) - The OpenId Connect `client_id`
- **redirect_uri** (required) - Where the callback handler is hosted
- **scope** *(optional)*: Reserved or custom claims to be returned in the tokens
- **response_type** *(optional)*: Desired token grant types
- **onAuthRequired** (optional)
- **auth** (optional) - Provide an Auth object instead of the options above. This is helpful when integrating `okta-react` with external libraries that need access to the tokens.

Accepts a callback to make a decision when authentication is required. If this is not supplied, `okta-react` redirects to Okta. This callback will receive `auth` and `history` parameters. This is triggered when:
1. `auth.login` is called
Expand Down Expand Up @@ -259,60 +262,60 @@ export default App;

`auth` provides methods that allow managing tokens and auth state. All of the methods return Promises.

* `auth.isAuthenticated()`
#### `auth.isAuthenticated()`

Returns `true` or `false`, depending on whether the user has an active access or id token.
Returns `true` or `false`, depending on whether the user has an active access or id token.

* `auth.getUser()`
#### `auth.getUser()`

Returns the result of the userinfo endpoint if an access token exists.
Returns the result of the OpenID Connect `/userinfo` endpoint if an access token exists.

* `auth.getIdToken()`
#### `auth.getIdToken()`

Retrieves the id token from storage if it exists.
Retrieves the id token from storage if it exists.

* `auth.getAccessToken()`
#### `auth.getAccessToken()`

Retrieves the access token from storage if it exists.
Retrieves the access token from storage if it exists.

* `auth.login()`
#### `auth.login(fromUri)`

Calls `onAuthRequired` or redirects to Okta if `onAuthRequired` is undefined.
Calls `onAuthRequired` or redirects to Okta if `onAuthRequired` is undefined. This method accepts a `fromUri` parameter to push the user to after successful authentication.

* `auth.logout()`
#### `auth.logout(uri)`

Removes all the tokens and redirects to `/`.
Terminates the user's session in Okta and clears all stored tokens. Accepts an optional `uri` parameter to push the user to after logout.

* `auth.redirect({sessionToken})`
#### `auth.redirect({sessionToken})`

Performs a redirect to Okta with an optional `sessionToken`.
Performs a redirect to Okta with an optional `sessionToken`.

Example:
```typescript
auth.redirect({
sessionToken: '{sampleSessionToken}'
});
```
```typescript
auth.redirect({
sessionToken: '{sampleSessionToken}'
});
```

* `auth.handleAuthentication()`
#### `auth.handleAuthentication()`

Parses tokens from the url and stores them.
Parses tokens from the url and stores them.

## Development

1. Clone the repo:
- `git clone [email protected]:okta/okta-oidc-js.git`
- `git clone [email protected]:okta/okta-oidc-js.git`
2. Install the dependencies with lerna (install with `npm i lerna -g`):
- `lerna bootstrap`
- `lerna bootstrap`
3. Navigate into the `okta-react` package:
- `cd packages/okta-react`
- `cd packages/okta-react`
4. Make your changes to `okta-react/src/`
5. Set the following environment variables:
- `ISSUER` - your authorization server
- `CLIENT_ID` - the client id of your app
- `USERNAME` - username of org user, needed if you want to run tests
- `PASSWORD` - password of org user, needed if you want to run tests
- `ISSUER` - your authorization server
- `CLIENT_ID` - the client id of your app
- `USERNAME` - username of org user, needed if you want to run tests
- `PASSWORD` - password of org user, needed if you want to run tests
6. Start a sample server:
- `npm start`
- `npm start`

## Commands

Expand Down
19 changes: 14 additions & 5 deletions packages/okta-react/src/Auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,16 @@ export default class Auth {

async getUser() {
const accessToken = this._oktaAuth.tokenManager.get('accessToken');
return accessToken ? this._oktaAuth.token.getUserInfo(accessToken) : undefined;
const idToken = this._oktaAuth.tokenManager.get('idToken');
if (accessToken && idToken) {
const userinfo = await this._oktaAuth.token.getUserInfo(accessToken);
if (userinfo.sub === idToken.claims.sub) {
// Only return the userinfo response if subjects match to
// mitigate token substitution attacks
return userinfo
}
}
return idToken ? idToken.claims : undefined;
}

async getIdToken() {
Expand All @@ -71,8 +80,8 @@ export default class Auth {
return accessToken ? accessToken.accessToken : undefined;
}

async login() {
localStorage.setItem('secureRouterReferrerPath', this._history.location.pathname);
async login(fromUri) {
localStorage.setItem('secureRouterReferrerPath', fromUri || this._history.location.pathname);
if (this._config.onAuthRequired) {
const auth = this;
const history = this._history;
Expand All @@ -81,10 +90,10 @@ export default class Auth {
await this.redirect();
}

async logout() {
async logout(path) {
this._oktaAuth.tokenManager.clear();
await this._oktaAuth.signOut();
this._history.push('/');
this._history.push(path || '/');
}

async redirect({sessionToken} = {}) {
Expand Down
12 changes: 9 additions & 3 deletions packages/okta-react/test/e2e/harness/e2e/App.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ describe('React + Okta App', () => {
protectedPage.waitUntilVisible();
expect(protectedPage.getLogoutButton().isPresent()).toBeTruthy();

protectedPage.waitForElement('userinfo-container');
protectedPage.getUserInfo().getText()
.then(userInfo => {
expect(userInfo).toContain('email');
});

// Logout
protectedPage.getLogoutButton().click();

Expand All @@ -62,11 +68,11 @@ describe('React + Okta App', () => {
password: process.env.PASSWORD
});

appPage.waitUntilVisible();
expect(appPage.getLogoutButton().isPresent()).toBeTruthy();
protectedPage.waitUntilVisible();
expect(protectedPage.getLogoutButton().isPresent()).toBeTruthy();

// Logout
appPage.getLogoutButton().click();
protectedPage.getLogoutButton().click();

appPage.waitUntilLoggedOut();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ export class ProtectedPage {
}

waitUntilVisible() {
browser.wait(ExpectedConditions.presenceOf(this.getHeader()), 5000);
browser.wait(ExpectedConditions.urlContains('/protected'), 5000);
}

getHeader() {
return element(by.tagName('h3'));

waitForElement(id) {
const el = element(by.id(id));
browser.wait(ExpectedConditions.presenceOf(el), 5000);
}

getLogoutButton() {
Expand All @@ -32,4 +33,8 @@ export class ProtectedPage {
getLoginButton() {
return element(by.id('login-button'));
}

getUserInfo() {
return element(by.id('userinfo-container'));
}
}
2 changes: 1 addition & 1 deletion packages/okta-react/test/e2e/harness/protractor.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ exports.config = {
capabilities: {
'browserName': 'chrome',
chromeOptions: {
args: ['--headless','--disable-gpu','--window-size=1600x1200', '--no-sandbox']
args: ['--headless', '--disable-gpu', '--window-size=1600x1200', '--no-sandbox']
}
},
directConnect: true,
Expand Down
15 changes: 13 additions & 2 deletions packages/okta-react/test/e2e/harness/src/Home.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ export default withAuth(class Home extends Component {

this.checkAuthentication = this.checkAuthentication.bind(this);
this.checkAuthentication();

this.login = this.login.bind(this);
this.logout = this.logout.bind(this);
}

async checkAuthentication() {
Expand All @@ -33,6 +36,14 @@ export default withAuth(class Home extends Component {
}
}

async login() {
this.props.auth.login('/protected');
}

async logout() {
this.props.auth.logout('/');
}

componentDidUpdate() {
this.checkAuthentication();
}
Expand All @@ -43,8 +54,8 @@ export default withAuth(class Home extends Component {
}

const button = this.state.authenticated ?
<button id="logout-button" onClick={this.props.auth.logout}>Logout</button> :
<button id="login-button" onClick={this.props.auth.login}>Login</button>;
<button id="logout-button" onClick={this.logout}>Logout</button> :
<button id="login-button" onClick={this.login}>Login</button>;

return (
<div>
Expand Down
25 changes: 23 additions & 2 deletions packages/okta-react/test/e2e/harness/src/Protected.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,27 @@
* See the License for the specific language governing permissions and limitations under the License.
*/

import React from 'react';
import React, { Component } from 'react';
import { withAuth } from '@okta/okta-react';

export default () => <h3>Protected</h3>;
export default withAuth(class Protected extends Component {
constructor(props) {
super(props);
this.state = { userinfo: null };
}

async componentDidMount() {
const claims = await this.props.auth.getUser();
const userinfo = JSON.stringify(claims, null, 4);
this.setState({ userinfo });
}

render() {
return (
<div>
<div> Protected! </div>
{this.state.userinfo && <pre id="userinfo-container"> {this.state.userinfo} </pre>}
</div>
);
}
});

0 comments on commit 40a82cc

Please sign in to comment.