nuxt-auth icon indicating copy to clipboard operation
nuxt-auth copied to clipboard

Add custom headers for refresh call

Open n0-m4d opened this issue 1 year ago • 4 comments

Describe the feature

I'd like to be able to send custom headers when nuxt sends a request to the refresh endpoint. Why? Besides sidebase-auth, applications might be protected by Basic Auth (especially during development or staging areas).

Side note: while /refresh does not send the Basic Auth header i setup for my regular api-calls, /getSession does. not sure why.

Would this be of interest? Or is there already a way to achieve this (besides configuring web server setup)?

How would you implement this?

Something like this would be sufficient, although a hook/callback might be more flexible:

// nuxt.config.ts
[...]
refresh: {
        isEnabled: true,
        endpoint: { 
          path: 'refresh', 
          method: 'POST', 
          headers: {
            'Authorization': 'Basic Yml0asFkZW46a3VuZGVuY2VqdGVy',
          }, 
        },
[...]

(I replaced the default token.headerName with another header name).

Additional information

  • [ ] Would you be willing to help implement this feature?

Provider

  • [ ] AuthJS
  • [ ] Local
  • [X] Refresh
  • [ ] New Provider

n0-m4d avatar Dec 16 '24 09:12 n0-m4d

Hi @n0-m4d , adding hooks for all the methods has been a feature many people anticipate and I personally want to implement.

I did an initial prototype a couple months ago already and wasn't satisfied by it. I would definitely come back to it in the next year, probably for the next minor release even if nothing else pops up :slightly_smiling_face:

I created #964 to gather all the usecases

phoenix-ru avatar Dec 19 '24 13:12 phoenix-ru

Hello I see a lot issues on the "custom headers" subject, but no one help me to do what i want.

Is there a way to just add custom headers to endpoint simply like that ?

            endpoints: {
                signIn: {
                    path: '/auth/token',
                    method: 'post',
                    headers: {
                        'x-api-key': process.env.API_KEY,
                    }, 
                },
                signOut: false,
                signUp: false,
                getSession: {
                    path: '/auth/session',
                    method: 'get',
                    headers: {
                        'x-api-key': process.env.API_KEY,
                    }, 
                },
            },

For signin y do this and it's ok :

    await signIn(
        {
            tmp,
        },
        {},
        {},
        { 'x-api-key': runtimeConfig.public.apiKey }
    )

And for getSession i didnt find a workaround.

Did i miss something ?

Thks.

bnbc avatar Mar 07 '25 11:03 bnbc

I made a temporary solution by modifying the getSession function of the useAuth.js file

async function getSession(getSessionOptions) {
    const nuxt = useNuxtApp();
    const config = useTypedBackendConfig(useRuntimeConfig(), "local");

    // EDIT
    //const { path, method } = config.endpoints.getSession;
    const { path, method, headers } = config.endpoints.getSession;

    const { data, loading, lastRefreshedAt, rawToken, token: tokenState, _internal } = useAuthState();
    let token = tokenState.value;
    token ??= formatToken(_internal.rawTokenCookie.value, config);
    if (!token && !getSessionOptions?.force) {
        loading.value = false;
        return;
    }

    // EDIT
    // const headers = new Headers(token ? { [config.token.headerName]: token } : void 0);  
    let customHeaders = new Headers(token ? { [config.token.headerName]: token } : void 0);
    if (headers) {
        for (let key in headers) {
            customHeaders.append(key, headers[key]);
        }
    }

    loading.value = true;
    try {
        // EDIT
        //const result = await _fetch(nuxt, path, { method, headers });
        const result = await _fetch(nuxt, path, { method, headers: customHeaders });
        
        const { dataResponsePointer: sessionDataResponsePointer } = config.session;
        data.value = jsonPointerGet(result, sessionDataResponsePointer);
    } catch (err) {
        if (!data.value && err instanceof Error) {
            console.error(`Session: unable to extract session, ${err.message}`);
        }
        data.value = null;
        rawToken.value = null;
    }
    loading.value = false;
    lastRefreshedAt.value = /* @__PURE__ */ new Date();
    const { required = false, callbackUrl, onUnauthenticated, external } = getSessionOptions ?? {};
    if (required && data.value === null) {
        if (onUnauthenticated) {
            return onUnauthenticated();
        }
        await navigateTo(callbackUrl ?? await getRequestURLWN(nuxt), { external });
    }
    return data.value;
}

bnbc avatar Mar 07 '25 12:03 bnbc

And i do the same for refresh :

async function refresh(getSessionOptions) {
    const nuxt = useNuxtApp();
    const config = useTypedBackendConfig(useRuntimeConfig(), "local");
    if (!config.refresh.isEnabled) {
        return getSession(getSessionOptions);
    }

    // EDIT
    //const { path, method } = config.refresh.endpoint;
    const { path, method, headers } = config.refresh.endpoint;

    const refreshRequestTokenPointer = config.refresh.token.refreshRequestTokenPointer;
    const { refreshToken, token, rawToken, rawRefreshToken, lastRefreshedAt } = useAuthState();

    // EDIT
    //const headers = new Headers({
    //    [config.token.headerName]: token.value
    //});
    let customHeaders = new Headers({
        [config.token.headerName]: token.value
    });
    if (headers) {
        for (let key in headers) {
            customHeaders.append(key, headers[key]);
        }
    }
    const response = await _fetch(nuxt, path, {
        method,
        // EDIT
        //headers,
        headers: customHeaders,
        body: objectFromJsonPointer(refreshRequestTokenPointer, refreshToken.value)
    });
    const tokenPointer = config.refresh.token.refreshResponseTokenPointer || config.token.signInResponseTokenPointer;
    const extractedToken = jsonPointerGet(response, tokenPointer);
    if (typeof extractedToken !== "string") {
        console.error(
            `Auth: string token expected, received instead: ${JSON.stringify(extractedToken)}. Tried to find token at ${tokenPointer} in ${JSON.stringify(response)}`
        );
        return;
    }
    if (!config.refresh.refreshOnlyToken) {
        const refreshTokenPointer = config.refresh.token.signInResponseRefreshTokenPointer;
        const extractedRefreshToken = jsonPointerGet(response, refreshTokenPointer);
        if (typeof extractedRefreshToken !== "string") {
            console.error(
                `Auth: string token expected, received instead: ${JSON.stringify(extractedRefreshToken)}. Tried to find refresh token at ${refreshTokenPointer} in ${JSON.stringify(response)}`
            );
            return;
        }
        rawRefreshToken.value = extractedRefreshToken;
    }
    rawToken.value = extractedToken;
    lastRefreshedAt.value = /* @__PURE__ */ new Date();
    await nextTick();
    return getSession(getSessionOptions);
}

bnbc avatar Mar 07 '25 14:03 bnbc