Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

401 (Unauthorized) when subscribing to private-channel (Vue/Laravel) #789

Open
Arturexe opened this issue Dec 27, 2023 · 0 comments
Open

Comments

@Arturexe
Copy link

Do you want to request a feature or report a bug?
Question/Bug

What is the current behavior?
I've set up a Vue3/Laravel app with a live-chat via Pusher which works over non-private channel chat. In the next step I want to use a private channel but something weird happens. The pusher.subscribe function that tries to send a request to /api/pusher/auth doesn't seem to handle the sanctum authorization correctly, resulting in:

POST http://localhost:8000/api/pusher/auth 401 (Unauthorized)
ajax @ pusher-js.js?v=1974b27b:676
(anonymous) @ pusher-js.js?v=1974b27b:3548
authorize @ pusher-js.js?v=1974b27b:1860
subscribe @ pusher-js.js?v=1974b27b:1828
subscribe @ pusher-js.js?v=1974b27b:3960
subscribeAll @ pusher-js.js?v=1974b27b:3951
(anonymous) @ pusher-js.js?v=1974b27b:3868
emit @ pusher-js.js?v=1974b27b:1230
updateState @ pusher-js.js?v=1974b27b:2341
connected @ pusher-js.js?v=1974b27b:2281
callback @ pusher-js.js?v=1974b27b:2176
cb @ pusher-js.js?v=1974b27b:2619
tryNextStrategy @ pusher-js.js?v=1974b27b:2459
(anonymous) @ pusher-js.js?v=1974b27b:2507
(anonymous) @ pusher-js.js?v=1974b27b:3399
finish @ pusher-js.js?v=1974b27b:1752
onMessage @ pusher-js.js?v=1974b27b:1729
emit @ pusher-js.js?v=1974b27b:1230
onMessage @ pusher-js.js?v=1974b27b:1327
socket.onmessage @ pusher-js.js?v=1974b27b:1343
Show 20 more frames
Show less
pusher-js.js?v=1974b27b:979 Pusher :  : ["Error: Unable to retrieve auth string from channel-authorization endpoint - received status: 401 from http://localhost:8000/api/pusher/auth. Clients must be authorized to join private or presence channels. See: https://pusher.com/docs/channels/server_api/authorizing-users/"]

This problem is specific to the pusher route, all other api routes work just fine.

Frontend setup

pusher.js:

import Pusher from 'pusher-js'

Pusher.logToConsole = true

const pusher = new Pusher('bf29be46d8eb2ea8ccd4', {
  cluster: 'eu',
  forceTLS: true,
  authEndpoint: 'http://localhost:8000/api/pusher/auth',
  withCredentials: true,
  wsPort: 443,
  wssPort: 443,
  enableStats: false,
  enabledTransports: ['ws', 'wss'],
  auth: {
    headers: {
      'X-Requested-With': 'XMLHttpRequest',
    },
  }
})

export default pusher

axios.js:

import axios from 'axios'

axios.defaults.withCredentials = true

if (import.meta.env.DEV) {
  axios.defaults.baseURL = 'http://localhost:8000'
}

the user is authenticated in a login component:

const signIn = () => {
  axios.get('/sanctum/csrf-cookie').then(() => {
    axios
      .post('/login', form)
      .then(() => {
        store.auth = sessionStorage.auth = 1
        store.signInModal = false
      })
      .catch((er) => {
        state.errors = er.response.data.errors
      })
  })
}

and the app tries to subscribe to pusher in a chat component, only accessible to authenticated users:

const inquireChatSession = () => {
  axios
    .get(`/api/chat/${props.id}`)
    .then((res) => {
      state.chatSessionId = res.data.id
      state.messages = res.data.messages
      state.loadingSession = false

      const channel = pusher.subscribe(`private-chat.${state.chatSessionId}`) // 401 happens here

      channel.bind('App\\Events\\ChatMessageSent', (data) => {
        state.messages.push(data.chatMessage)
      })
    })
    .catch((er) => {
      state.errors = er.response.data.errors
      state.loadingSession = false
    })
}

Backend setup

/routes/api.php:

Route::post('/pusher/auth', function (Request $request) {
    \Log::info('test');

    $user = $request->user();
    if (!$user) {
        abort(403, 'Unauthorized');
    }

    $pusher = new Pusher(
        env('PUSHER_APP_KEY'),
        env('PUSHER_APP_SECRET'),
        env('PUSHER_APP_ID'),
        ['cluster' => env('PUSHER_APP_CLUSTER')]
    );

    $channelName = $request->channel_name;
    $socketId = $request->socket_id;

    $auth = $pusher->socket_auth($channelName, $socketId);

    return response()->json(['auth' => $auth]);
})->middleware('auth:sanctum');

pusher is trying to connect to this route but the authorization fails, resulting in 'test' not being logged and returning aforementioned error back to the client. This seems to be a pusher + sanctum problem because connecting to this diagnostic route works:

Route::post('/pusher/auth', function (Request $request) {
    \Log::info(var_export($request, true));
    \Log::info('Request headers: ', $request->header());
    \Log::info('Request cookies: ', $request->cookies->all());
    \Log::info('Session data: ', $request->session()->all());
    \Log::info('User: ', $request->user());
})

but $request->cookies->all() is empty and $request->user() is null. For some reason no auth cookies are arriving in the pusher route. To check if sanctum works by itself, connecting to the following route, returns the authorized user:

Route::middleware('auth:sanctum')->get('/test-auth', function (Request $request) {
    return $request->user();
});

relevant .env entries:

APP_URL=http://localhost:8000
FRONTEND_URL=http://localhost:3000
SANCTUM_STATEFUL_DOMAINS=localhost:3000
SESSION_DOMAIN=localhost

PUSHER_APP_ID=1728518
PUSHER_APP_KEY=bf29be46d8eb2ea8ccd4
PUSHER_APP_SECRET=...
PUSHER_HOST=
PUSHER_PORT=443
PUSHER_SCHEME=https
PUSHER_APP_CLUSTER=eu

like I said, the entire authentication of the app works fine except the pusher route.

Browser Network Tab

auth request #1:

General:

Request URL:       http://localhost:8000/api/pusher/auth
Request Method:    OPTIONS
Status Code:       204 No Content
Remote Address:    127.0.0.1:8000
Referrer Policy:   strict-origin-when-cross-origin

Response Headers:

Access-Control-Allow-Credentials:    true
Access-Control-Allow-Headers:        x-requested-with
Access-Control-Allow-Methods:        POST
Access-Control-Allow-Origin:         http://localhost:3000
Access-Control-Max-Age:              0
Cache-Control:                       no-cache, private
Connection:                          close
Content-Type:                        text/html; charset=UTF-8
Date:                                Wed, 27 Dec 2023 02:25:13 GMT
Host:                                localhost:8000
Vary:                                Access-Control-Request-Method, Access-Control-Request-Headers
X-Powered-By:                        PHP/8.3.1

Request Headers:

Accept:                            */*
Accept-Encoding:                   gzip, deflate, br
Accept-Language:                   en-GB,en;q=0.9,de;q=0.8
Access-Control-Request-Headers:    x-requested-with
Access-Control-Request-Method:     POST
Cache-Control:                     no-cache
Connection:                        keep-alive
Host:                              localhost:8000
Origin:                            http://localhost:3000
Pragma:                            no-cache
Referer:                           http://localhost:3000/
Sec-Fetch-Dest:                    empty
Sec-Fetch-Mode:                    cors
Sec-Fetch-Site:                    same-site
User-Agent:                        Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36

followed by auth request #2:

General:

Request URL:        http://localhost:8000/api/pusher/auth
Request Method:     POST
Status Code:        401 Unauthorized
Remote Address:     127.0.0.1:8000
Referrer Policy:    strict-origin-when-cross-origin

Response Headers:

Access-Control-Allow-Credentials:    true
Access-Control-Allow-Origin:         http://localhost:3000
Cache-Control:                       no-cache, private
Connection:                          close
Content-Type:                        application/json
Date:                                Wed, 27 Dec 2023 02:25:13 GMT
Host:                                localhost:8000
Set-Cookie:                          XSRF-TOKEN=eyJpdiI6Inl5T2ZLbndpZG1OUTV5MmxNdDlNNWc9PSIsInZhbHVlIjoiUzdJYVkzZzJvM3FnaUlIUGxVWFBDTTZYeHQveTBWOWoxSEsvcThGM00wVDh6WExmK2RYWVBldTNxK2xKS1RrV1JSTHA2b0NEMVFtQzlzSmxyVVVRbmlrSmNRdmJQaW00cWpIQVFyZkhYM0RwampuMDZWVzJsV3NUZjVJZ1kxaG0iLCJtYWMiOiI4MzFlZjBjYWZkNDZkZDBhMGYxZDgwMDQ5YTgzY2ExNDg1NDMyNjFlNTNmZDg5NGJmZTI4MDMxNzAzMjVlNjZjIiwidGFnIjoiIn0%3D; expires=Wed, 27 Dec 2023 04:25:13 GMT; Max-Age=7200; path=/; domain=localhost; samesite=lax
Set-Cookie: soul_meatcom_session=eyJpdiI6Ii8wTktnTVFMZUZBYXVTeTFTRjd4dmc9PSIsInZhbHVlIjoicDUzNjFJVEYyTVR5cXdrTGZQZWZ1NzF4UEZ6QUJXSWF3YUsya0lUZy9qb0IwNk0rM0cwa3RwV1YyZ1Q0T0JqWW90cjZKd2d3OXNqOW13aGswc2tPMGw0d0hPRkxDZDdqamFUQWpKSktVd2ZpS1c2b3NqQm5WMVhoK2VsLzJWeEkiLCJtYWMiOiI4YjM3ZWEwZWMwNzlhNDIxMWNhNjBhMjAzNzcxNDM0NGMxNTczOTU1YWQ0ZGFjNzEyYWJkNDI2ZWJiNjI2ZTZkIiwidGFnIjoiIn0%3D; expires=Wed, 27 Dec 2023 04:25:13 GMT; Max-Age=7200; path=/; domain=localhost; httponly; samesite=lax
X-Powered-By:                        PHP/8.3.1

Request Headers:

Accept:                   */*
Accept-Encoding:          gzip, deflate, br
Accept-Language:          en-GB,en;q=0.9,de;q=0.8
Cache-Control:            no-cache
Connection:               keep-alive
Content-Length:           87
Content-Type:             application/x-www-form-urlencoded
Host:                     localhost:8000
Origin:                   http://localhost:3000
Pragma:                   no-cache
Referer:                  http://localhost:3000/
Sec-Ch-Ua:                "Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"
Sec-Ch-Ua-Mobile:         ?0
Sec-Ch-Ua-Platform:       "macOS"
Sec-Fetch-Dest:           empty
Sec-Fetch-Mode:           cors
Sec-Fetch-Site:           same-site
User-Agent:               Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
X-Requested-With:         XMLHttpRequest

Browser Application Tab

there are two cookies in /storage/cookies/http://localhost:3000:

#1 soul_meatcom_session:

eyJpdiI6ImI4dUNWL0pCRmRibFMzNUdyT0JrL3c9PSIsInZhbHVlIjoiQTN4L0djZm52bjlCSFV4TmM0QU1oNUFoT0JBUlFGcGNWMFVwSDEvTFZBWmZwVi9kSEFnUitHcit6MzRLNXVkNkxLU1o5a0VhWmJ2OTNvYUdxMkpyVDVUcVZoQWRzckVlVi84Tis3UTdxazhkR0ozU1EyeldnaFowcStTRFFJYjgiLCJtYWMiOiJkNjc3MGM1ODc2MWM1NWFiMDBlNjYzMTg0OWI3M2RiZmNmZGU5NzU4Y2QzZDA0NmViZDQzZjIzODBiMWZiYWM1IiwidGFnIjoiIn0%3D

#2 XSRF-TOKEN:

eyJpdiI6Ik1xc0tCckEzS2RNNURFVWJ5aGc2Z0E9PSIsInZhbHVlIjoidVE4TElScENjRFlEbUFtVk1sVzZ1MGU5WDI2NXk2b214aEpWbU10K1hJUGZtQzdFOFBHV3JYblZiYmlFSmQvaSt2ZWQ5cWtjOXhtZXJQTmQ0NUNSVjAvQ2xmVDNwcUw0dkRFMHZnclRSc08wanVqaHdlbGFWeE5JMk1pTzRXOFgiLCJtYWMiOiJkNzQwODAwYzU2ZGE0OTVjNzQ0MjQxNzAwZDIxMGVkNGNkZTJjNWI2NjQ2YjMzZjk4NGM1YzI4MWJhOWZmMGI2IiwidGFnIjoiIn0%3D

Any idea what the problem is with the pusher route or how I could further debug this? Thank you in advance

What is the expected behavior?
Private channel subscription should work.

**Which versions of Pusher, and which browsers / OS are affected by this issue?
Tested Pusher versions: newest, 8.3, 7.6
MacOS: Ventura 13.2.1
Browser: Chrome

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant