Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/webqit/webflo-oauth2-client

Webflo OAuth2 library for Node.js
https://github.com/webqit/webflo-oauth2-client

nodejs oauth oauth2 oauth2-client webflo

Last synced: about 2 months ago
JSON representation

Webflo OAuth2 library for Node.js

Awesome Lists containing this project

README

        

# Webflo OAuth2 Client

NPM version
NPM downloads

Webflo OAuth2 library for Node.js.

## Installation

```shell
npm i @webqit/webflo-oauth2-client
```

## Usage

```js
import { ConsentFlow } from '@webqit/webflo-oauth2-client';
```

### Initialization

```js
const createConsentFlow = ( httpEvent, fetch ) => {

// Client app config
const client = {
// The application's base URL. E.g. http://localhost:3000/ (local), https://example.com/ (production)
baseUrl: process.env.OAUTH2_CALLBACK_HOST,
// Application's auth-flow endpoints, relative to base URL
callbacks: {
// The route where you'll handle signed-in callback from provider screen. (See below: The "Signed-In" Callback Route)
signedIn: '/auth',
// The route where you'll handle signed-out callback from provider screen. (See below: A "Sign Out" Route)
signedOut: '/',
},
};

// Provider: auth0
const auth0 = {
// Auth0-issued client ID
clientId: process.env.AUTH0_CLIENT_ID,
// Auth0-issued client secret
clientSecret: process.env.AUTH0_CLIENT_SECRET,
// OAuth2 server base URL. E.g. https://example.us.auth0.com/
baseUrl: process.env.AUTH0_BASE_URL,
// Auth0-issued auth-flow endpoints, relative to base URL
endpoints: {
signIn: '/authorize',
token: '/oauth/token',
revoke: undefined,
signOut: '/v2/logout',
},
// Parameters for verifying Auth0-issued ID Tokens
jwtokens: { jwksUrl: '.well-known/jwks.json', }
};

// Provider: google
const google = {
// Google-issued client ID
clientId: process.env.GOOGLE_CLIENT_ID,
// Google-issued client secret
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
// OAuth2 server base URL. Always https://oauth2.googleapis.com/ for Google
baseUrl: process.env.GOOGLE_BASE_URL,
// Google-issued auth-flow endpoints, relative to base URL
endpoints: {
signIn: 'https://accounts.google.com/o/oauth2/v2/auth',
token: '/token',
revoke: '/revoke',
signOut: undefined,
},
// Parameters for verifying Google-issued ID Tokens
jwtokens: { jwksUrl: 'https://www.googleapis.com/oauth2/v3/certs', }
};

// Auth instance
return new ConsentFlow( httpEvent, {
client, providers: { google/* as default provider */, auth0 },
}, fetch );

};
```

### Initialization Route

At the application's root handler, you'd initialize oauth2 client into the `context` object that is passed around:

```js
export default function( httpEvent, context, next, fetch ) {
context.oauth2 = createConsentFlow( httpEvent, fetch );
if ( next.pathname ) return next( context );
return { titleBar: 'Home' };
}
```

### A Protected "Sign In" Route

To protect a route, you'd call the `signIn()` method with optional fields - `{ provider /* defaults to the name of the first provider in list */, scope, audience }`:

```js
export default function( httpEvent, context, next, fetch ) {
return context.oauth2.signIn( { /* optional */scope: 'openid' }, session => {
// Authenticated...
// otherwise this function is not called, and user is redirected to provider sign in screen
// session is same as context.oauth2.session

// Are we going to a protected child page?
// session can always be accessed as context.oauth2.session down the hierarchy
if ( next.pathname ) return next( context );

// See what's in auth session
console.log( session ); // { access_token, token_type: 'Bearer', scope, provider, info, ...etc }
// See what's in session.info
console.log( session.info ); // { iss, sub: , exp, ...etc }
// (If "profile", "email" were in the value of the "scope" parameter (as an array or a space-delimitted string), other infos would be available: email, name, avatar_url, etc.)

// Call an API endpoint at provider
const provider = context.oauth2.client.provider( /* specify provider name, otherwise default provider is implied */ );
const endpoint = '/userinfo'; // Or if defined in provider settings object above: endpoint = provider.endpoints.userinfo
const user = await provider.call( session.access_token, endpoint, { ...optionalBody } );

/*
Or if you wish...
const user = await fetch( 'https://oauth2.example.com/endpoint', {
body: JSON.stringify( { ...optionalBody } ),
headers: { 'Content-Type': 'application/json', Authorization: session.access_token }
} ).then( res => res.json() );
*/

// Show accounts page
return {
titleBar: 'My Account',
email: user.email,
}
} );
}
```

The context.oauth2.signIn() function could also be called without a callback...

```js
export default function( httpEvent, context, next, fetch ) {
let session, redirect, sessionOrRedirect = context.oauth2.signIn( { /* optional */scope: 'openid' } );
if ( sessionOrRedirect instanceof httpEvent.Response ) {
// User is being redirected to provider sign in screen
return ( redirect = sessionOrRedirect /* formality assignment */ );
}
// Authenticated...
session = sessionOrRedirect;
// session is same as context.oauth2.session

// Are we going to a protected child page?
// session can always be accessed as context.oauth2.session down the hierarchy
if ( next.pathname ) return next( context );

// See what's in auth session
console.log( session ); // { access_token, token_type: 'Bearer', scope, provider, info, ...etc }
// See what's in session.info
console.log( session.info ); // { iss, sub: , exp, ...etc }
// (If "profile", "email" were in the value of the "scope" parameter (as an array or a space-delimitted string), other infos would be available: email, name, avatar_url, etc.)

// Call an API endpoint at provider
const provider = context.oauth2.client.provider( /* specify provider name, otherwise default provider is implied */ );
const endpoint = '/userinfo'; // Or if defined in provider settings object above: endpoint = provider.endpoints.userinfo
const user = await provider.call( session.access_token, endpoint, { ...optionalBody } );

/*
Or if you wish...
const user = await fetch( 'https://oauth2.example.com/endpoint', {
body: JSON.stringify( { ...optionalBody } ),
headers: { 'Content-Type': 'application/json', Authorization: session.access_token }
} ).then( res => res.json() );
*/

// Show accounts page
return {
titleBar: 'My Account',
email: user.email,
}
}
```

### The "Signed In" Callback Route

To handle the "signed-in" redirect from *provider* screen - at the specified callback URL `client.callbacks.signedIn`:

```js
export default function( httpEvent, context, next ) {
if ( context.oauth2.isSigningIn() /* Detects if a sign-in session is ongoing */ ) {
// Performs "authorization_code" grant and redirects user back to the original protected route - where signIn() was called
return context.oauth2.handleSignInCallback();
}
// Returns the context.oauth2.session object or false
return context.oauth2.isSignedIn()?.info || {};
}
```

### A "Sign Out" Route

To perform "sign out" at any route:

```js
export default function( httpEvent, context, next ) {
if ( context.oauth2.isSignedIn() ) {
// User is signed-out (and is redirected to provider's sign-out URL, where given),
// then redirected back to "/" as specified in client.callbacks.signedOut
return context.oauth2.signOut();
}
return next();
}
```

## Full Documentation

Full documentation, including integrating other providers, coming soon.

## Issues

To report bugs or request features, please submit an issue to this repository.

## License

MIT.