Custom authentication in CAP with social logins
Hey everyone, a little while ago I prepared a small demo for CAP that shows how to add a custom authentication handler, and how to use that to add a social login to your CAP project. In this case, I chose to integrate a Github oAuth app. In this post, I’ll describe exactly what you need to do to integrate an external authentication provider of your own. The following only applies to NodeJS. For the JAVA people, I apologise but you’re on your own.
If you’d like to skip ahead, there’s a live demo here that uses VueJS and Tailwind for the front end, and all the code is in our Github. Feel free to clone and dive right in.
This post will take you through the following 4 steps:
- Create the oAuth application on Github.
- Create your CAP application and add a custom server object so you can add Passport
- Add a custom authentication handler to your CAP configuration
- Add a custom login button to your front end
The reason I chose passport is that it’s old and proven technology, and there are at the time of posting 520 different authentication strategies available for you to integrate so you can choose whichever one suits you.
Jorg has over 14 years of experience working so we consider Jorg as one of our technical gurus. He is passionate about all things software development. Jorg coordinates designs and implements automated solutions that improve efficiency across all SAP systems
Result
Just to show the results of this exercise, this is our app:
Calling the service without being logged in and without cookies set, CAP is going to present a 401 on the service.
After logging in, cookies are present and CAP will let you through
oAuth flow in CAP / Express
Creating an oAuth application
Since I chose Github as the oAuth provider, I simply followed the steps in the documentation. The process is straight forward. This is the path to follow:
The most important parts to get right are through the homepage, especially the redirect URL:
For development purposes, it is fine to put your local link in there like http://localhost:4004/auth/github/callback
. Just remember to have a development version and a production version, or to switch URL’s.
Create a CAP application, add a custom server and add Passport
The first thing to do after creating a new CAP app is to install the missing packages from NPM:
|
The documentation and examples on how to create a custom server or extend the existing server so you can work with the default Express app is pretty clear. If you’d like to read up on that the documentation click here, you’ll find my version below.
Here are the relevant files in the project we’ll be discussing:
.env
First of all, we need a place to safely store some secrets. The best place to store secrets is in a .env
file. Don’t forget to add it to your .gitignore
therefore, your secrets are kept away from your Git repositories. In our example, we have the following:
|
The callback URL is specific to your host, but the local one works fine when you’re testing this on your own machine.
srv/server.js
The custom server loads our server implementation. I separated those so I can reuse the implementation in Jest tests.
|
srv/serverImplementation.js
|
Routes we’re adding here
So we’re adding several routes:
auth/error
, this will get called when an error occursauth/logout
, this is the route that resets the session and removes the cookieauth/my-user
is the route that returns the current user as it is stored on the sessionauth/github
is the authenticating method, This will hand over to passport and tell passport to present the Github login screen and check the Github access token, if one is present. Passport in this case does all the heavy lifting, initiating the redirect to Github and asks for your username and password.auth/github/callback
is the method that’s configured on the Github oAuth client, these have to match. If the callback on the oAuth client is not identical to the method on your CAP server, the redirections are not completed and your app will fail. After the user authenticates with Github, Passport takes over to turn the response from Github into a user session for the Express application to use.
/auth/passport.js
const GitHubStrategy = require(‘passport-github2’).Strategy;
//methods to indicate how to serialise and deserialise the user object. passport.serializeUser(function (user, done) { done(null, user); }); passport.deserializeUser(function (user, done) { done(null, user); });
passport.use(new GitHubStrategy({ clientID: GITHUB_CLIENT_ID, clientSecret: GITHUB_CLIENT_SECRET, callbackURL: GITHUB_CALLBACK_URL }, function (accessToken, refreshToken, profile, done) { return done(null, profile); }
|
The implementation of the passport strategy needs three things:
- Passport needs to know how to serialise and deserialise the user. In our case there’s really nothing to do but sometimes this user information could be encoded
- We need to initialise the Github strategy with the client ID, client secret and callback, so Passport can redirect us to the correct oAuth application
- We need a callback to tell passport what to do. Since we’re not doing anything flash we’re just telling Passport that we’re done and we’ll use the returning profile
In many cases this callback should be extended by checking if we have a local user in our Users
table already, for instance, so that we can create a profile that is not just the GitHub profile.
Add a custom authentication handler to your configuration
After setting up passport in the custom server object, CAP has a default security middleware. In Express, middleware is a function that runs on a route before the default route implementation is executed. In order to specify your own and override the default, add the following to your .cdsrc.json
:
|
The documentation on custom authentication methods is here.
Finally, we’ll have a look at the custom handler:
./auth/handler
module.exports = (req, res, next) => { |
This snippet converts the user
object on the request, fetched and deserialised by Passport into a CDS.User
object. CAP uses this user to call its own methods, such as role check:
req.user.is('admin') |
If the user
object is not of type cds.User
the app will probably crash, since methods like the above are used internally.
Add a custom login button to your front end
Front-end wise, there’s not much to do. You’ll only have to direct the user to route /auth/github
and Passport will take over the redirections to our oAuth client, any button or link is fine. To log out, simply add a link to /auth/logout
to remove the cookie and the session.
Conclusion
That’s it, the CAP app is now protected by the GitHub social login. To generalise the steps you need to take to replace the standard authentication with a social login or external oAuth are:
- Create the external oAuth application. There are many out there, and this demo uses Github
- Initialise the routes you’ll need to start the authentication process, the callbacks etcetera. Check with passport to select a strategy that fits your needs
- Replace the existing authentication middleware to avoid clashes
- Integrate the your new login into your front end
Thanks for making it this far. Here’s again a link to the demo and the Github repository. If you have any questions feel free to leave a comment.
[/et_pb_text][/et_pb_column][/et_pb_row][/et_pb_section]