If you want to create custom Yandex OAuth2
authorization in SAP CAP CDS Application
, this article will help you a lot.
Creating an app
First of all you need to create an app on this page: https://oauth.yandex.ru/client/new
. After that you need to get from there next parameters:
- Client ID
- Client Secret Key
- Callback URL
Configurating
You need to install two modules before we start creating custom authentication.
npm install passport cookie-session
Also you need to install passport-strategy package. In this article we used Yandex OAuth2
, that’s why you need to run next command:
npm install passport-yandex
And in the end you need to modify package.json
to let the cds
module to know, that you are using custom authentication:
"cds": {
"requires": {
"auth": {
"impl": "./auth/handler.js"
}
}
}
Passport strategy
Create a directory auth
and put there two files: passport.js
and handler.js
.
In the passport.js
file you need to paste following code:
const passport = require('passport');
const YandexStrategy = require('passport-yandex').Strategy;
const { YANDEX_CALLBACK_URL, YANDEX_CLIENT_ID, YANDEX_CLIENT_SECRET } = process.env;
passport.serializeUser(function (user, done) {
done(null, user);
});
passport.deserializeUser(function (user, done) {
done(null, user);
});
passport.use(new YandexStrategy({
clientID: YANDEX_CLIENT_ID,
clientSecret: YANDEX_CLIENT_SECRET,
callbackURL: YANDEX_CALLBACK_URL
},
function(accessToken, refreshToken, profile, done) {
process.nextTick(function () {
// here you can add an validation for existing of user in your database or something else.
return done(null, profile);
});
}
));
Here we created serializer and deserializer methods for user object and added passport strategy.
In handler.js
file we are watching if user exists in cookie-session and creating cds.User
for him. If logging in failed we return error Unauthorized
with status code 401
.
const cds = require("@sap/cds");
module.exports = (req, res, next) => {
if (req?.user) {
req.user = new cds.User(req.user);
next();
} else {
res.status(401).send();
}
}
Creating authorization logic
Finally you need to create server.js
file in srv/
directory and add the following code there:
const cds = require("@sap/cds");
const implementation = require('./serverImplementation');
cds.on("bootstrap", async (app) => await implementation(app));
module.exports = cds.server;
Also you should create serverImplementation.js
file in the same directory. In this file we will add logic for authentication process.
const passport = require('passport');
const cookieSession = require('cookie-session');
require('../auth/passport');
module.exports = async (app) => {
const { YANDEX_CLIENT_ID, YANDEX_CLIENT_SECRET } = process.env;
app.use(cookieSession({
name: 'yandex-auth-session',
keys: ['key1', 'key2']
}));
app.use(passport.initialize());
app.use(passport.session());
app.get('/error', function(req, res){
// you can implement error here
res.send('Current user does not exist');
});
app.get('/auth/my-user', (req, res) => {
// showing user details in json format
res.json(req?.user?._json)
})
app.get('/auth/yandex',
passport.authenticate('yandex'),
function(req, res){
// The request will be redirected to Yandex for authentication, so this
// function will not be called.
});
app.get('/auth/yandex/redirect',
passport.authenticate('yandex', { failureRedirect: '/error' }),
function(req, res) {
res.redirect('/');
});
app.get('/logout', function(req, res){
req.session = null;
req.logout();
res.redirect('/');
});
}
First of all you need to create and initialize cookie-session
for keeping data of authenticated user. Now let’s see the routes in the code above:
/auth/yandex
route is using for redirect user to the yandex authorization page./auth/yandex/redirect
- this route should be the same asCallback URL
in your yandex application. It uses for sending the code, which user will get after successful authorization on yandex page, and getting the access and refresh tokens. After that this method send access token to theYandex API
and getting back user’s data and redirect user to the root page/
./error
this route is using for catching the error./logout
this route is using for clearing the session and logout the user./auth/my-user
this route shows user information in json format.
That would be nice if you could make your code a little bit more expressive.