OAuth Authentication Issue with ImmoScout API – “Inadequate OAuth consumer credentials”

Question:
Hi everyone, I’m trying to integrate with the ImmoScout API using OAuth 1.0a but I’m running into an issue. My current setup is using express, axios, and the oauth-1.0a package.

The problem:
When I try to get the request token or access token, I receive the following error response:

<common:messages xmlns:common="http://rest.immobilienscout24.de/schema/common/1.0">
    <message>
        <messageCode>ERROR_BAD_REQUEST</messageCode>
        <message>Inadequate OAuth consumer credentials.</message>
    </message>
</common:messages>

My code:
Below is the code I’m using to authenticate and make API calls:

const express = require('express');
const bodyParser = require('body-parser');
const OAuth = require('oauth-1.0a');
const axios = require('axios');
const crypto = require('crypto');

const app = express();
app.use(bodyParser.urlencoded({ extended: true }));

const SANDBOX_URL = "https://rest.sandbox-immobilienscout24.de";
const REQUEST_TOKEN_URL = `${SANDBOX_URL}/restapi/security/oauth/request_token`;
const ACCESS_TOKEN_URL = `${SANDBOX_URL}/restapi/security/oauth/access_token`;
const ACCESS_CONFIRMATION_URL = `${SANDBOX_URL}/restapi/security/oauth/confirm_access`;
const RESOURCE_ENDPOINT_URL = `${SANDBOX_URL}/restapi/api/offer/v1.0/user/me/realestate/`;

const CLIENT_KEY = '';
const CLIENT_SECRET = '';

let requestTokenRepository = {};
let accessTokenRepository = {};

const oauth = OAuth({
    consumer: { key: CLIENT_KEY, secret: CLIENT_SECRET },
    signature_method: 'HMAC-SHA1',
    hash_function(baseString, key) {
        return crypto.createHmac('sha1', key).update(baseString).digest('base64');
    },
});

// Initialize token exchange
app.get('/initialize-token-exchange', async (req, res) => {
    const callbackUrl = `http://localhost:${process.env.PORT || 3000}/callback`;

    const requestTokenParams = {
        url: REQUEST_TOKEN_URL,
        method: 'POST',
        data: { oauth_callback: callbackUrl },
    };

    try {
        const requestTokenResponse = await axios(requestTokenParams);
        const requestToken = requestTokenResponse.data;
        const userName = req.user.name;

        requestTokenRepository[userName] = requestToken;
        res.redirect(`${ACCESS_CONFIRMATION_URL}?oauth_token=${requestToken.oauth_token}`);
    } catch (error) {
        console.error(error);
        res.status(500).send('Error obtaining request token');
    }
});

// OAuth callback handling
app.get('/callback', async (req, res) => {
    const { state, oauth_token, oauth_verifier } = req.query;
    const userName = req.user.name;

    if (state !== 'authorized') return res.status(401).send('Authorization was rejected.');

    const latestRequestToken = requestTokenRepository[userName];

    if (!latestRequestToken || latestRequestToken.oauth_token !== oauth_token) {
        return res.status(400).send('Invalid request token.');
    }

    try {
        const accessTokenParams = {
            url: ACCESS_TOKEN_URL,
            method: 'POST',
            data: { oauth_verifier },
            headers: oauth.toHeader(oauth.authorize(requestTokenParams, latestRequestToken)),
        };

        const accessTokenResponse = await axios(accessTokenParams);
        accessTokenRepository[userName] = accessTokenResponse.data;

        res.redirect('/load-real-estates');
    } catch (error) {
        console.error(error);
        res.status(500).send('Error exchanging for access token');
    }
});

// Load real estates
app.get('/load-real-estates', async (req, res) => {
    const userName = req.user.name;
    const accessToken = accessTokenRepository[userName];

    if (!accessToken) return res.redirect('/initialize-token-exchange');

    try {
        const resourceParams = {
            url: RESOURCE_ENDPOINT_URL,
            method: 'GET',
            headers: oauth.toHeader(oauth.authorize(resourceParams, accessToken)),
        };

        const realEstatesResponse = await axios(resourceParams);
        res.send(realEstatesResponse.data);
    } catch (error) {
        console.error(error);
        res.status(500).send('Error loading real estates');
    }
});

// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
    console.log(`Server is running on http://localhost:${PORT}`);
});

Questions:

  1. Is there anything wrong with my OAuth implementation or the way I’m signing the requests?
  2. Could this issue be related to incorrect scopes, permissions, or API configuration?
  3. Are there specific headers or additional parameters I might be missing?

I’d appreciate any help or guidance to resolve this issue! Thanks in advance.

What I’ve tried:

  1. Double-checked the CLIENT_KEY and CLIENT_SECRET to ensure they are correct.
  2. Used both sandbox and production URLs.
  3. Made sure that the callback URL is correctly set to http://localhost:3000/callback.
  4. Validated my request token logic, but I still get the “Inadequate OAuth consumer credentials” error.