FeathersJS and Google OAuth 2.0
Implementing authentication may seem daunting with the number of libraries and modules available today. By understanding the purpose of each module, an OAuth 2.0 flow can be created with minimal configuration. The following is an implementation of the OAuth 2.0 authentication flow using Google and FeathersJS. The example being constructed will:
- Expose an endpoint to begin OAuth flow;
- Allow a user to confirm which Google account will be used;
- Create a user based on requested Google account data;
- Create a JWT for use in future requests.
Although a boilerplate project can be created using the FeathersJS CLI, all files in this article are created manually to illustrate each step.
Project setup
The directory hierarchy is arbitrary, however, there is a common theme in Feathers applications to assist in code organization. The directory structure for this example is as follows:
This article focuses on the /authentication
service. To follow along, clone the full source and checkout the initial hash.
Modules
Feathers authentication is an abstraction of PassportJS. As such, Passport plugins are able to be used in Feathers applications. For this example, the below packages are needed and can be installed with npm or yarn.
> npm install @feathersjs/authentication @feathersjs/authentication-jwt \ @feathersjs/authentication-oauth2 passport-google-oauth2
yarn add @feathersjs/authentication @feathersjs/authentication-jwt \ @feathersjs/authentication-oauth2 passport-google-oauth2
Adding authentication
Register a Google OAuth application
Following the guide provided by Google for setting up OAuth applications, there are four specific fields to verify:
- Client ID
- Client Secret
- Authorized JavaScript Origins
- Authorized redirect URIs
The client ID and secret are provided by Google after registering a new application. The origin and redirect are specified by the author of the new application. For this example, a custom port of 9001
is used along with the default authentication route of /auth/google/callback
. The callback route is utilized by Google after a user has been authenticated.
A new section, authentication
, is added to /config/default.json
. The newly created client ID and secret are stored here for use later.
Note: “authentication”, “google”, “clientID”, and “clientSecret” are arbitrary keys.
Create a secure JWT secret
Any string can be used to sign a JWT. The larger the secret is, in relation to the JWT body, the more secure. For more information regarding JWTs, see https://jwt.io. This application will use a random, hex string, generated from the crypto
library in NodeJS. From a Node console:
node> crypto.randomBytes(256).toString('hex')
The JWT secret is also stored in /config/default.json
:
Note: It is recommended to hold these values in environmental variables as to not expose secure keys to source control. The key “secret” is also an arbitrary name.
With the above configuration, the authentication service can be created and configured. The default path provided by Feathers is /auth/<provider>
for OAuth requests and /authentication
to create JWTs.
Using authentication hooks
With a completed /authentication
service and using the authenticate
hook provided with @feathersjs/authentication
, existing services can now be secured.
A pre-existing /items
service utilizing the authenticate
hook:
Seeing authentication in action
Using Postman and Chrome, the OAuth 2.0 flow can be demonstrated.
To verify the JWT hooks are working as expected, a request is made to /items
. Since no JWT is present, this request fails:
Visiting /auth/google
starts the OAuth process to verify a user and create a corresponding JWT:
Switching these same requests into Chrome, a user is able to be logged in through Google, which successfully calls the /auth/google/callback
route. This call returns a newly created JWT:
Using this JWT, a request can now be made to the /items
and /users
services:
Making the same request to /users
displays the user created based on the information returned by the Google OAuth system. If using multiple OAuth providers, such as Google and Facebook, it may also be required to normalize user data. This can be done with a custom hook as described in the Feathers documentation.
This token can be stored at the client application’s discretion, typically in LocalStorage or a cookie, and sent as a Bearer token
in the Authorization
header on future requests. If FeathersJS is used on the client, the flow requires minimal integration. This will be discussed in a future article within this series.
Conclusion
Feathers provides a minimal API for registering services. Creating robust authentication may feel like piecing together a puzzle, however, once familiarized with the purpose of the underlying modules can be a quick, community supported solution.
Follow the FeathersJS series with Aquil.io as the client implementation and other authentication flows will be described.
Example code
The full working example used for this article can be found here.
This example was built on Node v9.x with the following dependencies:
References
- https://jwt.io/introduction
- https://support.google.com/googleapi/answer/6158849
- https://docs.feathersjs.com/api/authentication/oauth2.html
- https://docs.feathersjs.com/api/services.html
- http://www.passportjs.org
- https://expressjs.com
- https://www.npmjs.com/package/config
- https://www.npmjs.com/package/body-parser
If you want to check out what I’m working on or have web development needs, visit Aquil.io.
Originally published at aquil.io.