Repository: auth0/nextjs-auth0 (ts)

Issue:
CallbackHandlerError after Organization Invite
### Checklist

- [X] The issue can be reproduced in the [nextjs-auth0 sample app](https://github.com/auth0-samples/auth0-nextjs-samples/tree/main/Sample-01) (or N/A).
- [X] I have looked into the [Readme](https://github.com/auth0/nextjs-auth0#readme), [Examples](https://github.com/auth0/nextjs-auth0/blob/main/EXAMPLES.md), and [FAQ](https://github.com/auth0/nextjs-auth0/blob/main/FAQ.md) and have not found a suitable solution or answer.
- [X] I have looked into the [API documentation](https://auth0.github.io/nextjs-auth0/) and have not found a suitable solution or answer.
- [X] I have searched the [issues](https://github.com/auth0/nextjs-auth0/issues) and have not found a suitable solution or answer.
- [X] I have searched the [Auth0 Community](https://community.auth0.com) forums and have not found a suitable solution or answer.
- [X] I agree to the terms within the [Auth0 Code of Conduct](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md).

### Description

**Context**: I am developing a Next.js multi-tenant application. Subdomains map to organizations. 

When inviting a user to an organization through Auth0 and following the invite  URL, I am hitting the `CallbackHandlerError: Missing state cookie from login request (check login URL, callback URL and cookie config).` It looks like the cookie is being set on the wrong domain but I'm not quite sure on this or why. The invite goes through and is accepted on Auth0's end, but on the frontend, the error is shown.


In my callback handler, I am using the subdomain as the `redirectUrl`, so something along the lines of https://sub.domain.com/api/auth/callback. **There are no issues completing a login request**, but when accepting an org invite, I run into the issues. My callback+login handler are using the same `redirectUri` and I'm using the subdomain URL as the `returnTo` (https://sub.domain.com)

When sending the invitation to an org, the invitation URL is something like https://domain.com/api/auth/login?invitation=...orginization=.... I noticed if I prepend the relevant subdomain to this invitation URL, everything works as expected. I understand the cookie is not getting set in the correct place and as a result the callback is not working but I don't understand where the cookie is actually being set and the reason for this.

I have played around with the cookie config a lot. Setting the cookie domain to `domain.com`/`.domain.com`/`sub.domain.com` does allow the invite to be accepted and redirect to occur properly but then I can't log out. Logging out seems to clear the cookies, but then they appear again straight away. I have also tried `SameSite=none` and `Secure=true` but to no avail.

Unfortunately it is not possible to append the organization to the Application Login URI on Auth0 the same way it is possible to do so on the Allowed Callback URLs. Not too sure on how to proceed here.

Thanks!

Login Handler
```js
login: handleLogin((request: NextRequest) => {
    const hostname = headers().get("host");
    const url = request.nextUrl;
    const searchParams = Object.fromEntries(url.searchParams.entries());
    const { invitation, organization, ...rest } = searchParams;
    let subdomain = getSubdomain(hostname) || rest.organization_name;
 
    return {
      authorizationParams: {
        invitation,
        redirect_uri: "https://sub.domain.com/api/auth/callback",
        organization: organization || organisationsMap.get(subdomain!),
        ...rest,
      },
      returnTo: "https://sub.domain.com",
    };
  }),

```
Callback Handler
```js
callback: handleCallback((request: NextRequest) => {
    const hostname = headers().get("host");
    const subdomain = getSubdomain(hostname);
    const url = request.nextUrl;
    const searchParams = Object.fromEntries(url.searchParams.entries());
    const { organization, ...rest } = searchParams;
    const redirectURL = "https://subdomain.domain/api/auth/callback";
    return {
      organization: organization || organisationsMap.get(subdomain!),
      redirectUri: redirectURL,
      authorizationParams: {
        ...rest,
        redirect_uri: redirectURL,
      },
    };
  }),
  ```

### Reproduction

1. Send invite via Auth0 dashboard
2. Follow invitation link in email or through dashboard
3. Enter new password
4. Hit error

### Additional context

_No response_

### nextjs-auth0 version

3.0.0

### Next.js version

13.4.12

### Node.js version

18.x

Pull request:
Fix for edge cookies delete not supporting domain or path
### 📋 Changes

`NextRespons.cookies.delete` currently doesn't support the `path` or `domain` options. Workaround this by using `NextRespons.cookies.set` (which does)

### 📎 References

See https://github.com/vercel/edge-runtime/pull/507
fixes #1337 


Patch:
diff --git a/src/http/auth0-next-response-cookies.ts b/src/http/auth0-next-response-cookies.ts
index faad23b8..470b5080 100644
--- a/src/http/auth0-next-response-cookies.ts
+++ b/src/http/auth0-next-response-cookies.ts
@@ -32,7 +32,7 @@ export default class Auth0NextResponseCookies extends Auth0ResponseCookies {
   public clearCookie(name: string, options?: CookieSerializeOptions) {
     const cookieSetter = cookies();
     try {
-      cookieSetter.delete({ ...options, name, value: '' });
+      cookieSetter.set({ ...options, name, value: '', expires: new Date(0) });
     } catch (_) {
       warn();
     }
diff --git a/src/http/auth0-next-response.ts b/src/http/auth0-next-response.ts
index cb037b0e..b7a9525d 100644
--- a/src/http/auth0-next-response.ts
+++ b/src/http/auth0-next-response.ts
@@ -13,7 +13,7 @@ export default class Auth0NextResponse extends Auth0Response<NextResponse> {
   }
 
   public clearCookie(name: string, options?: CookieSerializeOptions) {
-    this.res.cookies.delete({ ...options, name, value: '' });
+    this.setCookie(name, '', { ...options, expires: new Date(0) });
   }
 
   public redirect(location: string, status = 302): void {


Test patch:
diff --git a/tests/http/auth0-next-response-cookies.test.ts b/tests/http/auth0-next-response-cookies.test.ts
index 1a20784e..6ccb4476 100644
--- a/tests/http/auth0-next-response-cookies.test.ts
+++ b/tests/http/auth0-next-response-cookies.test.ts
@@ -34,6 +34,24 @@ describe('auth0-next-response', () => {
     expect(res.headers.get('set-cookie')).toEqual('foo=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT');
   });
 
+  it('should delete cookies with a domain', async () => {
+    const cookies = new Auth0NextResponseCookies();
+    const res = new NextResponse();
+    jest.mocked(nextCookies).mockImplementation(() => res.cookies as any);
+    cookies.clearCookie('foo', { domain: 'example.com' });
+    expect(res.headers.get('set-cookie')).toEqual(
+      'foo=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Domain=example.com'
+    );
+  });
+
+  it('should delete cookies with a path', async () => {
+    const cookies = new Auth0NextResponseCookies();
+    const res = new NextResponse();
+    jest.mocked(nextCookies).mockImplementation(() => res.cookies as any);
+    cookies.clearCookie('foo', { path: '/foo' });
+    expect(res.headers.get('set-cookie')).toEqual('foo=; Path=/foo; Expires=Thu, 01 Jan 1970 00:00:00 GMT');
+  });
+
   it('should not throw when deleting a cookie fails', async () => {
     const cookies = new Auth0NextResponseCookies();
     jest.mocked(nextCookies).mockImplementation(
diff --git a/tests/http/auth0-next-response.test.ts b/tests/http/auth0-next-response.test.ts
index 8a17d0c0..43b93d88 100644
--- a/tests/http/auth0-next-response.test.ts
+++ b/tests/http/auth0-next-response.test.ts
@@ -34,11 +34,29 @@ describe('auth0-next-response', () => {
     expect(auth0Res.res.headers.get('set-cookie')).toEqual(['foo=bar; Path=/', 'baz=qux; Path=/'].join(', '));
   });
 
-  it('should clear cookies', async () => {
+  it('should delete a cookie', async () => {
     const [, res] = setup();
     const auth0Res = new Auth0NextResponse(res);
     auth0Res.clearCookie('foo');
 
-    expect(auth0Res.res.headers.get('set-cookie')).toMatch(/foo=;.*Expires=.*1970/);
+    expect(auth0Res.res.headers.get('set-cookie')).toBe('foo=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT');
+  });
+
+  it('should delete a cookie with domain option', async () => {
+    const [, res] = setup();
+    const auth0Res = new Auth0NextResponse(res);
+    auth0Res.clearCookie('foo', { domain: 'example.com' });
+
+    expect(auth0Res.res.headers.get('set-cookie')).toBe(
+      'foo=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Domain=example.com'
+    );
+  });
+
+  it('should delete a cookie with path option', async () => {
+    const [, res] = setup();
+    const auth0Res = new Auth0NextResponse(res);
+    auth0Res.clearCookie('foo', { path: '/foo' });
+
+    expect(auth0Res.res.headers.get('set-cookie')).toBe('foo=; Path=/foo; Expires=Thu, 01 Jan 1970 00:00:00 GMT');
   });
 });