Back

How to build a public SAP S/4HANA integration: Building the Auth Flow

Aug 3, 20249 minute read

Hey there, fellow JavaScript aficionado! Ready to dive into the world of SAP S/4HANA integration? Today, we're going to focus on one of the most crucial aspects of building a public integration: the authorization flow. Buckle up, because we're about to make your integration secure and user-friendly!

Introduction

SAP S/4HANA is a powerhouse of business processes, and integrating it with your applications can open up a world of possibilities. But before we can tap into that goldmine of data, we need to ensure our users can securely authenticate. That's where our authorization flow comes in – it's the gatekeeper that keeps the bad guys out and lets the good guys in.

Prerequisites

Before we jump in, make sure you've got:

  • Access to an SAP S/4HANA system (I know, easier said than done sometimes!)
  • A Node.js environment set up and ready to go
  • A solid grasp on OAuth 2.0 (but don't worry, we'll refresh your memory as we go)

Setting up the Development Environment

First things first, let's get our project structure in order. Create a new directory for your project and initialize it with npm:

mkdir sap-s4hana-integration cd sap-s4hana-integration npm init -y

Now, let's install the libraries we'll need:

npm install express axios dotenv

We're using Express for our server, Axios for HTTP requests, and dotenv for managing environment variables. Trust me, your future self will thank you for using dotenv!

SAP S/4HANA OAuth 2.0 Configuration

Before we write any code, we need to set up our OAuth client in the SAP system. This process can vary depending on your SAP setup, but generally, you'll need to:

  1. Log into your SAP S/4HANA system
  2. Create a new OAuth client
  3. Note down the client ID and client secret

Keep these credentials safe – they're the keys to your kingdom!

Implementing the Authorization Flow

Initiating the OAuth flow

Let's start by setting up our Express server and creating a route to initiate the OAuth flow:

require('dotenv').config(); const express = require('express'); const axios = require('axios'); const app = express(); const port = 3000; app.get('/auth', (req, res) => { const authUrl = `${process.env.SAP_AUTH_URL}?client_id=${process.env.CLIENT_ID}&response_type=code&redirect_uri=${encodeURIComponent(process.env.REDIRECT_URI)}`; res.redirect(authUrl); }); app.listen(port, () => console.log(`Server running on port ${port}`));

This route constructs the authorization URL and redirects the user to the SAP login page. Make sure to set up your .env file with the necessary variables!

Handling the callback

After the user logs in, SAP will redirect them back to your application with an authorization code. Let's handle that:

app.get('/callback', async (req, res) => { const { code } = req.query; try { const tokenResponse = await axios.post(process.env.SAP_TOKEN_URL, { grant_type: 'authorization_code', client_id: process.env.CLIENT_ID, client_secret: process.env.CLIENT_SECRET, code, redirect_uri: process.env.REDIRECT_URI }); // Store the tokens securely (more on this later) const { access_token, refresh_token } = tokenResponse.data; res.send('Authentication successful!'); } catch (error) { console.error('Error exchanging code for token:', error); res.status(500).send('Authentication failed'); } });

Token management

Now that we have our tokens, we need to store them securely and manage their lifecycle. In a production environment, you'd want to use a secure storage solution, but for now, let's keep it simple:

let tokens = {}; function storeTokens(accessToken, refreshToken) { tokens = { accessToken, refreshToken, expiresAt: Date.now() + 3600000 }; // Assuming 1-hour expiry } async function getValidAccessToken() { if (Date.now() >= tokens.expiresAt) { await refreshAccessToken(); } return tokens.accessToken; } async function refreshAccessToken() { try { const response = await axios.post(process.env.SAP_TOKEN_URL, { grant_type: 'refresh_token', client_id: process.env.CLIENT_ID, client_secret: process.env.CLIENT_SECRET, refresh_token: tokens.refreshToken }); storeTokens(response.data.access_token, response.data.refresh_token); } catch (error) { console.error('Error refreshing token:', error); throw error; } }

Making Authenticated Requests

Now that we have our auth flow set up, making authenticated requests is a breeze:

app.get('/api/example', async (req, res) => { try { const accessToken = await getValidAccessToken(); const response = await axios.get(`${process.env.SAP_API_URL}/your-endpoint`, { headers: { Authorization: `Bearer ${accessToken}` } }); res.json(response.data); } catch (error) { console.error('API request failed:', error); res.status(500).send('API request failed'); } });

Error Handling and Security Considerations

Always be prepared for things to go wrong. Implement proper error handling for common OAuth errors like invalid_grant or invalid_token. Also, remember that in a real-world scenario, you'd want to use a more secure method of token storage, like encrypted cookies or a secure database.

Testing the Integration

Don't forget to test your integration thoroughly! Set up a test environment that mimics your production setup, and write unit tests for your auth flow. Here's a simple example using Jest:

const axios = require('axios'); jest.mock('axios'); test('getValidAccessToken refreshes expired token', async () => { tokens = { accessToken: 'old', refreshToken: 'refresh', expiresAt: Date.now() - 1000 }; axios.post.mockResolvedValue({ data: { access_token: 'new', refresh_token: 'newRefresh' } }); const token = await getValidAccessToken(); expect(token).toBe('new'); expect(axios.post).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({ grant_type: 'refresh_token', refresh_token: 'refresh' })); });

Conclusion

And there you have it! You've just built a robust authorization flow for your SAP S/4HANA integration. Remember, this is just the beginning – there's a whole world of SAP APIs out there waiting for you to explore. Keep iterating, keep learning, and most importantly, keep coding!

Additional Resources

Happy integrating, and may your tokens always be fresh and your API calls always successful!