The grant was issued to another client. Please make sure the ‘client_id’ matches the one used at the authorize request

I am new to okta. and I have a problem as follows:
I have configured Okta application for both frontend (SPA application) and backend (web application) in Okta Console.

  1. Create Frontend Application (SPA)
    Log in to Okta Developer Console.
    Go to: Applications > Applications.
    Select Create App Integration.
    Select OIDC – OpenID Connect > Single-Page App (SPA)

  2. Create Backend Application (Web Application)
    Log in to Okta Developer Console.
    Go to: Applications > Applications.
    Select Create App Integration.
    Select OIDC – OpenID Connect > Web

Here is an example of how I setup the Frontend side

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Okta SPA App</title>
</head>
<body>
    <h1>Okta SPA OAuth2 Example</h1>
    <button id="login">Login with Okta</button>
    <button id="logout" style="display:none;">Logout</button>
    <p id="status"></p>
    <script src="https://cdn.jsdelivr.net/npm/@okta/[email protected]/dist/okta-auth-js.min.js"></script>

    <script>
        const oktaAuth = new OktaAuth({
            issuer: 'https://{myOktaDomain}}/oauth2/default',
            clientId: 'my_client_id',
            redirectUri: 'http://localhost:8080/callback',
            responseType: ['code'],
            scopes: ['openid', 'profile', 'email']
        });
        document.addEventListener('DOMContentLoaded', () => {
            const loginButton = document.getElementById('login');
            const logoutButton = document.getElementById('logout');
            const statusElement = document.getElementById('status');

            loginButton.addEventListener('click', () => {
                console.log('Login button clicked');
                statusElement.innerText = 'Redirecting to Okta...';
                oktaAuth.token.getWithRedirect({
                    responseType: 'code',
                    scopes: ['openid', 'profile', 'email']
                }).catch(err => {
                    console.error('Error during redirect:', err);
                    statusElement.innerText = 'Error during login.';
                });
            });

            logoutButton.addEventListener('click', () => {
                console.log('Logout button clicked');
                statusElement.innerText = 'Logging out...';
                oktaAuth.signOut().catch(err => {
                    console.error('Error during logout:', err);
                    statusElement.innerText = 'Error during logout.';
                });
            });

            handleCallback();
        });

        async function handleCallback() {
            if (window.location.pathname === '/callback') {
                console.log('Handling callback...');
                try {
                    const tokenResponse = await oktaAuth.token.parseFromUrl();
                    const authorizationCode = new URL(window.location.href).searchParams.get('code');
                    
                    console.log('Authorization Code:', authorizationCode);
                    document.getElementById('status').innerText = `Authorization Code: ${authorizationCode}`;
                    document.getElementById('login').style.display = 'none';
                    document.getElementById('logout').style.display = 'block';
                } catch (err) {
                    console.error('Error during callback handling:', err);
                    document.getElementById('status').innerText = 'Error during login process.';
                }
            }
        }
    </script>
</body>
</html>

This is my backend side:

const express = require('express');
const axios = require('axios');
const bodyParser = require('body-parser');
const app = express();

const CLIENT_ID = 'my_clientId'; 
const CLIENT_SECRET = 'my_clientsecret'; 
const REDIRECT_URI = 'http://localhost:8080/callback'; 

// Middleware
app.use(bodyParser.json());

app.get('/callback', async (req, res) => {
    const authorizationCode = req.query.code;

    if (!authorizationCode) {
        return res.status(400).send('Authorization code is missing.');
    }

    try {
        const tokenResponse = await axios.post('https://{myOktaDomain}/oauth2/default/v1/token', new URLSearchParams({
            'grant_type': 'authorization_code',
            'code': authorizationCode,
            'redirect_uri': REDIRECT_URI,
            'client_id': CLIENT_ID,
            'client_secret': CLIENT_SECRET
        }), {
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded'
            }
        });

        const accessToken = tokenResponse.data.access_token;
        console.log('Access Token:', accessToken);
        res.json({ message: 'Login successful', accessToken });

    } catch (error) {
        console.error('Error exchanging authorization code for token:', error.response ? error.response.data : error.message);
        res.status(500).json({ 
            error: 'Error exchanging authorization code for token', 
            details: error.response ? error.response.data : error.message 
        });
    }
});

app.get('/health', (req, res) => {
    res.send('Server is running.');
});

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

my problem is that even though i have configured client_id correctly for both frontend (spa application) and backend (web application) in okta console but when i run the program i keep getting error

Error exchanging authorization code for token: {
  error: 'invalid_grant',
  error_description: "The grant was issued to another client. Please make sure the 'client_id' matches the one used at the authorize request."
}

can anyone please help me find the cause

I want the Frontend to provide the user interface for logging in via Okta, including navigating the user to the Okta page for authorization and handling the callback response. The Backend receives the authorization code from the frontend, then exchanges that code with Okta to get the access token and responds to the frontend