javascriptreactjsreact-admin

client (React Admin) not consistent sending authorization header to backend, when making a request


Hi There dear programmer,

I am having a issue with the client not being consistent with sending the authorization header when sending a request (for a resource).

What I mean with not being consistent is that it sometimes does send a request with the correct Authorization that I have specified in the client. At times refreshing works but this is very unconsistent en not what a user want to deal with.

I cant really figure out why the token not found/ being send. As soon as I log in the application successfully it will receive a token from the backend which I store in memory (on the frontend).

What I have tried is within the getAuthHeaders function, I have set a if else to check if there is a token, if there is No token then I check the available refreshtokens and then refresh it if there any found. After the refreshing is successfull then I refresh the token and then add the token to the header to be used in a request.

I could not really find any related issue online so I decided to ask this question here.

If there are any questions, just ask so I can try clarify the issue more clear.

Kind regards,

const getAuthHeaders = () => {
    const token = inMemoryJwt.getToken()
    const headers = new Headers({"Content-Type": "application/ld+json"})
    
    if(token){ 
        headers.set("X-Authorization", token)
    } else {
        if(inMemoryJwt.checkAvailableRefreshToken()){
            inMemoryJwt.getRefreshedToken()
            inMemoryJwt.waitForTokenRefresh().then(() => {
                // headers.set("Content-Type", "application/json")
                headers.set('X-Authorization', `${inMemoryJwt.getToken()}`)
            })
        }
    }    
    return headers;
};

const getHydraWithHeaders = (url, options = {}) =>
    {
        console.log({urlSet: url})
        return fetchHydra(url, {
            ...options,
            withCredentials: true, 
            headers: getAuthHeaders(),
        })
    }
    

const apiDocumentationParser = async () => {
    try {
        // setRedirectToLogin(false)    
        return await parseHydraDocumentation("/api", {
            headers: getAuthHeaders(),
        })
    } catch (result) {
        const { api, request, status } = result;
        if (status !== 401 || !request) {
        throw result;
        }
    
        inMemoryJwt.ereaseToken()
    
        return {
            api,
            request,
            status,
        }
    }
}

export const dataProvider = ({
    ...hydraDataProvider({
        entrypoint: '/api',
        httpClient: getHydraWithHeaders,
        useEmbedded: true,
        apiDocumentationParser: apiDocumentationParser
    }),

Header response: when no Authheader is added

access-control-allow-headers:
Content-Type, Accept, Origin, X-Authorization

access-control-allow-methods:
GET, POST, OPTIONS, PATCH, PUT, DELETE
access-control-allow-origin:*
access-control-expose-headers:*
alt-svc:h3=":443"; ma=2592000
cache-control:no-cache, private
content-length:44
content-range:*
content-type:application/json
date:Thu, 28 Nov 2024 15:32:06 GMT
server:Caddy
status:401 Unauthorized
www-authenticate:Bearer
x-debug-token:0acbca
x-debug-token-link:https://localhost/_profiler/0acbca
x-powered-by:PHP/8.3.11
x-robots-tag:noindex

response:

code:401
message: "JWT Token not found"

react-admin error on page when no token is send :

Cannot read properties of undefined (reading 'length')

Solution

  • Your current implementation of getAuthHeaders can't work, because you are using asynchronous code (inMemoryJwt.waitForTokenRefresh().then(() => { /*...*/ })) in a synchronous method (getAuthHeaders). By the time the refreshed token arrives, it's already too late, the headers have already been returned.

    Instead, you probably need to implement your token refreshing mechanism using both addRefreshAuthToAuthProvider and addRefreshAuthToDataProvider, provided by react-admin. There, you'll be able to use asynchronous code.