Authorization for laravel passport through websocket - laravel-5

I have a private channel. And i can use it only if i authenticated on site. But in case of laravel passport i have no idea how it should be work. By default all what i need contains in cookies.
What i have:
Broadcast::routes();
Broadcast::channel('App.User.{id}', function ($user, $id) {
return (int) $user->id === (int) $id;
});
Broadcast::routes(['middleware' => ['api']]); have no effect.
How do I authenticate users through laravel passport, for that would be connected to a private channel websocket?

I been through this, and my solution was using Laravel Echo & Laravel Echo Server for SocketIo.
Retrieve the user access token, then store it in cookie or application storage, and inject it while you instantiating or re-instantiating Laravel Echo, like the following:
let Echo = require('laravel-echo')
window.Echo = new Echo({
broadcaster: 'socket.io',
host: window.location.hostname + ':6001'
auth: {
headers: {
Authorization: 'Bearer ' + AUTH_API_TOKEN,
},
},
});
References:
https://laravel.com/docs/5.3/broadcasting#driver-prerequisites
https://github.com/tlaverdure/laravel-echo-server

Unfortunately at the current moment the echo does not support authorization with the help of laravel passport, it is necessary to write your own sokket.io server.

Related

Laravel Sanctum XHR request returns me a 401 - "Unauthenticated."

I am trying out Laravel Sanctum and unfortunately I have a problem and hope you can help me.
I have several blade pages that I call up via the web route. For example '/', 'about-us' and /dashboard. The dashboard is only visible to users who are logged in.
web.php
Route::get('/dashboard', function () {
return view('dashboard');
})->middleware(['auth'])->name('dashboard');
In the dashboard, I have a tab where the logged-in user can view all other users. Here I send a get fetch call to the api route with Vanilla JS.
api.php
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});
My JS in the blade file looks like this:
async function send(method, url = "/api/users") {
const rawResponse = await fetch(url, {
method: method,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}
}, 2000);
const content = await rawResponse.json();
return content;
}
Problem: But I get a 401 here Object { message: "Unauthenticated." }. Why and what do I have to do to make it work?
My thinking so far.
As I understand it, Laravel sets a laravel_session and an XSRF-token cookie when it is called. If I fire the request against the webroute, laravel recognises me as the user. But if i fire the same request against the laravel api route i am not logged in for laravel. So I have to provide something in my request. I had tried to include the XSRF token in the request header. Like that:
const token = document.querySelector('meta[name="csrf-token"]').getAttribute('content')
// ...
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'X-CSRF-TOKEN': token
},
But that didn't work either.
Note: I have not configured anything else in Laravel to sanctum. I only ran the migration (personal_access_token). But the table is always empty.
My Kernel.php file:
'api' => [
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],

Laravel - Pusher Authentication - Server Side

I'm working on an chat application with 2 components: a client side that is a mobile app and a server side that is developed with Laravel. Currently I can't seem to figure out how to do the pusher authentication side on the server for a user on a specific channel. After figuring the documentation I found that the authentication is done via js call from the frontend with the socket-id but for my application I need to do the authentication from the server because I'm keeping a history of the messages in a local DB. What I don't find is how can I do this on the server.
The code I use for authentication is just authenticating the client on db based on a key I created in the channel enrol:
public function authorizeUser(Request $request, $groupId, $conversationId) {
/*
$socket_id = $request->get('socket_id');
*/
$user = Auth::user();
$conversation = Conversation::find($conversationId)->where('group_id', $groupId)->first();
$user_conversation = DB::table('user_conversation')
->where('conversation_id', $conversation->id)
->where('user_id', $user->id)
->first();
if($user_conversation->authorized) {
return response()->json(['response' => 'You are authorized!'], 201);
}
$accessToken = Crypt::decryptString($request->get('key'));
//dd($conversation->access_token.' '.$accessToken);
if(Hash::check($accessToken, $conversation->access_token)) {
$conversation->users()
->wherePivot('authorized', false)
->updateExistingPivot($user->id,[
'authorized' => true
]);
// Some how, here I should authorize the user for pusher
return response()->json(['response' => 'You have been authorized!'], 201);
}
}
The documentation states that I should use the routes/channels.php file to authenticate the users but from the server side I can't use Laravel Echo for creating a connection. This is done automatically from the broadcast.php file where I have set the env for the pusher.
Thank you,
Armand

Laravel echo admin channel authentication error

I'am using Laravel 5.5 with echo for an admin panel ,
the main guard (web) is related to Admin class ,
the private channel name that i want to get is Admin.1 ,
The problem is when i change the channel name from
Echo.private('App.User.' + this.userid)
to
Echo.private('App.Admin.' + this.userid)
it gets me this error
broadcasting/auth 500 (Internal Server Error)
what i tried is :
i put this
Broadcast::routes(['middleware' => [ 'api', 'web','auth' ] ]);
in web.php and api.php and BroadcastServiceProvider.php put it doesn't seem to work
i also tried this soltion
How to define or pass auth guard for broadcast authentication routes instead of default auth guard?
In your echo settings set authEndpoint to 'broadcasting/auth'.
here's the code that works for me after facing the same problem.
new Echo({
broadcaster: 'pusher',
key: '{{env("PUSHER_APP_KEY")}}',
cluster: 'us2',
encrypted: true,
authEndpoint: '{!! env("APP_URL") !!}/broadcasting/auth',
});
I solve my problem by :
adding this to BroadcastServiceProvider
public function boot()
{
Broadcast::routes();
require base_path('routes/channels.php');
/*
* for auth user channel
*/
Broadcast::channel('App.Admin.{id}',function($user, $id){
return (int) $user->id === (int) $id;
});
}

Protect laravel api service with Okta and JWT

Trying to put together a solution to protecting a Laravel 5.4 api using OKTA and JWT. I have an SPA that logs into my application via OKTA and retrieves an access_token and id_token. It also passes this to API calls in the header using 'Authorization': Bearer ${accessToken} but now i am struggling to find a solution to verify this access token with OKTA within the Laravel backend. been looking at tymon/jwt-auth but cant workout how to add a custom solution to verifiy the token but i would assume it can be done using okta/jwt-verifier does anyone have any samples/guide? also looked at laravel/socialite and socialiteproviders/okta but that seems more about a traditional backend login rather than an SPA
Our okta/jwt-verifier library should be able to help you out here. You will have to create a custom middleware solution to capture and authorize the request based on the bearer token. Once you have that middleware set up, inside of the verifier library, you can run the following to verify the accessToken.
$jwtVerifier = (new \Okta\JwtVerifier\JwtVerifierBuilder())
->setAudience('api://default')
->setClientId('{clientId}')
->setIssuer('https://{yourOktaDomain}.com/oauth2/default')
->build();
$jwt = $jwtVerifier->verify($jwt);
By changing the client id and your okta domain above, you should be able to pass in the accessToken to the verify method. If you do not get any exceptions, you can assume that the jwt is valid and approve the request.
See the github repo readme for information about what you have access to once you verify the validity of the JWT
For those finding this post. In the SPA make sure you also define the issuer, this should be a useful start...
//react login
this.oktaAuth = new OktaAuth({
url: props.config.oktaUrl
,clientId:props.config.clientId
,redirectUri:props.config.redirectUri
,issuer: props.config.issuer
});
this.oktaAuth.signIn({
username: this.state.username,
password: this.state.password
})
.then((response) => {
if (response.status === 'SUCCESS') {
this.setState({
sessionToken: response.sessionToken
});
this.oktaAuth.token.getWithoutPrompt({
responseType: ['id_token', 'token']
,scopes: ['openid', 'email', 'profile']
,sessionToken: response.sessionToken
})
.then((tokenOrTokens) => {
this.setState({
tokenOrTokens: tokenOrTokens
});
window.localStorage.setItem('access_token', tokenOrTokens[1].accessToken);
})
.catch(function(err) {
console.log('err', err);
});
}
})
//api call
const accessToken = window.localStorage.getItem('access_token') || null;
const config = {
method: 'GET',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': `Bearer ${accessToken}`
},
};
fetch(url, config)
.then((response) => {
...
//laravel api route
Route::group(['prefix' => 'restricted', 'middleware' => ['okta.validate']], function() {
Route::get('/getprotecteddata', 'MyController#getProtectedData');
});
//laravel kernel.php
protected $routeMiddleware = [
...
'okta.validate' => \App\Http\Middleware\ValidateOKTAToken::class,
];
//laravel middleware
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class ValidateOKTAToken
{
public function handle($request, Closure $next)
{
$token = $this->parseAuthHeader($request);
$jwt = $this->validate($token);
\Log::info("ValidateOKTAToken jwt=" . json_encode($jwt->toJson()));
return $next($request);
}
protected function validate($token) {
$oktaClientId = env('OKTA_CLIENTID');
$oktaIssuer = env('OKTA_ISSUER');
$oktaAudience = env('OKTA_AUDIENCE');
$jwtVerifier = (new \Okta\JwtVerifier\JwtVerifierBuilder())
->setAudience($oktaAudience)
->setClientId($oktaClientId)
->setIssuer($oktaIssuer)
->build();
$jwt = $jwtVerifier->verify($token);
return $jwt;
}
protected function parseAuthHeader(Request $request, $header = 'authorization', $method = 'bearer')
{
$header = $request->headers->get($header);
if (! starts_with(strtolower($header), $method)) {
return false;
}
return trim(str_ireplace($method, '', $header));
}
}

POST http://localhost:8000/broadcasting/auth 403 (Forbidden)

I am trying to make my app connecting to pusher on a private channel.
But I am getting the following error in console:
POST http://localhost:8000/broadcasting/auth 403 (Forbidden)
app.js
/**
* First we will load all of this project's JavaScript dependencies which
* includes Vue and other libraries. It is a great starting point when
* building robust, powerful web applications using Vue and Laravel.
*/
require('./bootstrap');
window.Vue = require('vue');
/**
* Next, we will create a fresh Vue application instance and attach it to
* the page. Then, you may begin adding components to this application
* or customize the JavaScript scaffolding to fit your unique needs.
*/
Vue.component('payment', require('./components/Payment.vue'));
Vue.component('form-ajax', require('./components/FormAjax.vue'));
Vue.component(
'passport-clients',
require('./components/passport/Clients.vue')
);
Vue.component(
'passport-authorized-clients',
require('./components/passport/AuthorizedClients.vue')
);
Vue.component(
'passport-personal-access-tokens',
require('./components/passport/PersonalAccessTokens.vue')
);
const app = new Vue({
el: '#app'
});
Echo.private(`articles.admin`)
.listen('ArticleEvent', function(e) {
console.log(e);
});
Error
What maybe the cause of the error and how to resolve it.
Error 403 /broadcasting/auth with Laravel version > 5.3 & Pusher, you need change your code in resources/assets/js/bootstrap.js with
window.Echo = new Echo({
broadcaster: 'pusher',
key: 'your key',
cluster: 'your cluster',
encrypted: true,
auth: {
headers: {
Authorization: 'Bearer ' + YourTokenLogin
},
},
});
And in app/Providers/BroadcastServiceProvider.php change by
Broadcast::routes()
with
Broadcast::routes(['middleware' => ['auth:api']]);
or
Broadcast::routes(['middleware' => ['jwt.auth']]); //if you use JWT
it worked for me, and hope it help you.
Have you tried to customise your authEndpoint.
this thing works on my end.
bootsrap.js
window.Echo = new Echo({
broadcaster: 'pusher',
// ...
authEndpoint: '/custom/endpoint/auth'
});
In my case I have used a custom auth guard which is causing the problem.
I have added the middleware to pass my custom auth guard and this solves the problem.
public function boot()
{
Broadcast::routes(['middleware' => 'auth:admin']);
require base_path('routes/channels.php');
}
This link explains more what's going on.
I added below code to routes/web.php and it worked.
Route::post('/broadcasting/auth', function () {
return Auth::user();
});
I Have faced same problem and this trick save my life.
go to api.php
Broadcast::routes(['predix' => 'api', 'middleware' => ['auth:api']]);

Resources