Alternate to Session Storage in Laravel Token Based API - php

I'm building an Electron app that connects to a Laravel 8 App (with Jetstream using Inertia) and the API.
I'm porting over the existing Inertia Jetstream Vue components to replicate the same functionality that appears in the actual web app.
When using the password confirm functionality Laravel uses session storage to store the time the password was confirmed, and then later again to check the status of the confirmed password.
When using token authentication, there is no session.
I can create new controllers to handle this no problem, but how can I substitute the calls to $request->session()?
The code to store the password confirmation looks like this:
public function store(Request $request)
{
$confirmed = app(ConfirmPassword::class)(
$this->guard, $request->user(), $request->input('password')
);
if ($confirmed) {
// here is the problem ... no session with tokens
$request->session()->put('auth.password_confirmed_at', time());
}
return $confirmed
? app(PasswordConfirmedResponse::class)
: app(FailedPasswordConfirmationResponse::class);
}
What's the best way to store this so it can be retrieved on subsequent calls?

I am not totally certain because it is used on mobile, which I have no experience with. However in browser land what you could do is to use cookies and set the token to httpOnly. I have a small example in nodeJs from one of my hobby projects:
const token = jwt.sign({ _id: currentUser._id }, process.env.JWT_SECRET, { expiresIn: '7d' });
currentUser.password = undefined;
res.cookie('token', token, {
httpOnly: true,
});
By setting the cookie to HttpOnly a user cannot fiddle with it. since it's mobile if it supports cookies this is even less a vulnarability issue.
Rodney

Related

Laravel 7 - Use REST API instead of a database

I am using a rest api to store/retrieve my data which is stored in a postgres database. The api is not laravel, its an external service!
Now i want to create a website with laravel (framework version 7.3.0) and i'm stuck on how to implement the api calls correctly.
For example: i want to have a custom user provider with which users can log-in on the website. But the validation of the provided credentials is done by the api not by laravel.
How do i do that?
Just make a Registration controller and a Login Controller by "php artisan make:controller ControllerName" and write Authentication logics there.
In previous versions of Laravel you had a command like "php artisan make:auth" that will make everything needed to do these operations. But in Laravel 7.0 you need to install a package called laravel/ui.
Run "composer required laravel/ui" to install that package
Then run "php artisan ui bootstrap --auth"
and now, you are able to run "php artisan make:auth"
This command will make whole Registration (Signup) and Login system for you.
and in orer to work with REST, you may need to know REST (Http) verbs. Learn about GET, POST, PUT, PATH, DELETE requests and how to make those request with PHP and Laravel collection methods. Learn about JSON parsing, encoding, and decoding. Then you can work with REST easily. and work without any template codes from other packages.
Thank you so much. I hope this answer give you some new information/thought. Thanks again.
Edit:
This might not be the best way. But this is what I did at that time. I tried curl and guzzle to build the request with session cookie and everything in the header to make it look like a request from a web browser. Couldn't make it work.
I used the web socket's channel id for the browser I want the changes to happen and concatenated it with the other things, then encrypted it with encrypt($string). After that, I used the encrypted string to generate a QR code.
Mobile app (which was already logged in as an authenticated used) scanned it and made a post request with that QR string and other data. Passport took care of the authentication part of this request. After decrypting the QR string I had the web socket's channel id.
Then I broadcasted in that channel with proper event and data. Caught that broadcast in the browser and reloaded that page with JavaScript.
/*... processing other data ...*/
$broadcastService = new BroadcastService();
$broadcastService->trigger($channelId, $eventName, encrypt($$data));
/*... returned response to the mobile app...*/
My BroadcastService :
namespace App\Services;
use Illuminate\Support\Facades\Log;
use Pusher\Pusher;
use Pusher\PusherException;
class BroadcastService {
public $broadcast = null;
public function __construct() {
$config = config('broadcasting.connections.pusher');
try {
$this->broadcast = new Pusher($config['key'], $config['secret'], $config['app_id'], $config['options']);
} catch (PusherException $e) {
Log::info($e->getMessage());
}
}
public function trigger($channel, $event, $data) {
$this->broadcast->trigger($channel, $event, $data);
}
}
In my view :
<script src="{{asset('assets/js/pusher.js')}}"></script>
<script src="{{asset('assets/js/app.js')}}" ></script>
<script>
<?php
use Illuminate\Support\Facades\Cookie;
$channel = 'Channel id';
?>
Echo.channel('{{$channel}}')
.listen('.myEvent' , data => {
// processing data
window.location.reload();
});
</script>
I used Laravel Echo for this.
Again this is not the best way to do it. This is something that just worked for me for that particular feature.
There may be a lot of better ways to do it. If someone knows a better approach, please let me know.
As of my understanding, you are want to implement user creation and authentication over REST. And then retrieve data from the database. Correct me if I'm wrong.
And I'm guessing you already know how to communicate over API using token. You are just stuck with how to implement it with laravel.
You can use Laravel Passport for the authentication part. It has really good documentation.
Also, make use of this medium article. It will help you to go over the step by step process.

SimpleSMLphp Custom Module Authentication Triggering Twice

Background: I am trying to set up single sign on (SSO) for users such that they can authenticate to my website and not have to authenticate a second time to our third-party MSP's website. Ideally, the user clicks a link on our website and is taken to the third-party site already logged in and landing on the dashboard (if the account doesn't exist, it is created during this step). We are not using SAML for authentication as a security feature, so all that we need the SAML code for is just producing cookies that prevent the user from having to log in again when he/she gets to our vendor's site. This third party MSP does not support authentication via API or web service and therefore I have been tasked with implementing SAML, their only supported SSO method. I am new to SAML (but not PHP or development) and have been learning as I go. I am told it will support the goals described above.
I initially tried using LDAP as the authentication source as this is what I use for authentication to my website, but this resulted in me getting directed to a login page with no discernible way to instead just pass parameters to SimpleSAMLphp to tell it "the user is already authenticated, all I need you to do is give me valid cookies so I can get past the third party website's authentication checks".
So I switched to writing a custom authentication module. I opened up the GitHub for SimpleSAMLphp and used the "UserPassBase" class as an example to create my own authentication module that inherits from the "Source" class. Because I don't need to re-authenticate the user against LDAP a second time since they're already logged in to our website, I created a simple "authenticate" function that just sets the $state['Attributes'] array.
Here is the code for my custom module:
<?php
namespace SimpleSAML\Module\productauth\Auth\Source;
use SimpleSAML\Auth;
/**
Author: Joey
Class developed to be used as a custom authentication module for simpleSAMLphp. This class will take an existing session from a product website and use it to create a SAML session and redirect to a website.
**/
class ProductAuth extends \SimpleSAML\Auth\Source {
const STAGEID = '\SimpleSAML\Module\productauth\Auth\ProductAuth.state';
const AUTHID = '\SimpleSAML\Module\productauth\Auth\ProductAuth.AuthId';
private $user;
public function __construct($info, $config) { // parameters aren't used, just filler from base class
$info = array("AuthId" => "productauth");
parent::__construct($info, $config);
}
public function login($user, $redirectURL) {
$this->user = $user; // normally I'd set this in the constructor, but the overload has my hands tied as far as function definitions go
$this->initLogin($redirectURL); // calls authenticate function and then, if no exceptions, parent::loginCompleted which redirects to the given URL
}
public function authenticate(&$state) { // called by parent::initLogin
$state[self::AUTHID] = $this->authId;
$state['Attributes'] = [
'uid' => [$this->user->uid],
'givenName' => [$this->user->givenName],
'sn' => [$this->user->sn],
'mail' => [$this->user->mail]
];
$id = Auth\State::saveState($state, self::STAGEID);
}
}
?>
I am calling it from a controller class on my website:
private function goToTrainingSite() {
require_once("../third-party-libs/simplesamlphp/_include.php");
global $TRAINING_URL;
$user = $_SESSION['subject']->user;
$samlObj = new SimpleSAML\Module\productauth\Auth\Source\ProductAuth(array(), array());
$samlObj->login($user, $TRAINING_URL);
}
I mimicked the flow of the "UserPassBase" class (https://github.com/simplesamlphp/simplesamlphp/blob/master/modules/core/lib/Auth/UserPassBase.php), but it seems that despite all of my authentication working and setting a SimpleSAMLAuth cookie, when the parent::loginCompleted function in the "Source" class (https://github.com/simplesamlphp/simplesamlphp/blob/master/lib/SimpleSAML/Auth/Source.php) runs, it redirected me to the third party site. I then see the following in the logs:
SAML2.0 - IdP.SSOService: incoming authentication request: [REDACTED DATA]
Session: 'productauth' not valid because we are not authenticated.
I have been trying for 3 days to figure out why it seems as though despite setting SimpleSAML session cookies with a completed, successful authentication, that upon receiving the auth request from the SP, my SimpleSAMLphp code just pretends to not know about the completed auth and tries to authenticate again... but because it is not being called from my code, it doesn't have access to the $user variable which contains all of the attributes I need to place on the user when he/she authenticates to this third party website. It seems that when it receives an authentication request, my SimpleSAMLphp installation starts a new session and tries a brand new authentication.
I have delved into a lot of the code of SimpleSAMLphp and tried to understand what is going on, but it seems that there is just no reasonable way to authenticate by calling an authentication source from PHP code and being able to skip the SP-initiated authentication. I have tried:
Using the SimpleSAML API (https://simplesamlphp.org/docs/stable/simplesamlphp-sp-api) to call my authentication source, but there seems to be no way to pass that $user variable I need the attributes from.
Trying to load the cookies in the "Session" class when it is checking for valid sessions... but it seems like the cookies from the successful auth session initiated by my code are just gone and nowhere to be found.
I decided to stop focusing on trying to get the $user variable and the data I needed to the second authentication, and instead focus on WHY the second authentication was even happening. I looked at the cookies and thought about how the data was being retrieved, and made a correct hunch that our application's custom session handler might be at fault for SimpleSAMLphp's inability to recognize the first authentication. Our custom session handler stores our sessions in the database, but SimpleSAMLphp expects to use the default PHP session handler to manage its session. Therefore, my first authentication was being sent to the database and when SimpleSAMLphp started looking for it where PHP sessions are usually stored, it didn't see it and assumed it needed to kick off another authentication session from scratch.
Using SimpleSAMLphp's documentation for service providers and a lot of my own debugging, I changed the function in my controller like so:
private function goToTrainingSite() {
require_once ("../third-party-libs/simplesamlphp/_include.php");
global $TRAINING_URL;
$joeySiteSession = $_SESSION;
$user = $_SESSION ['subject']->user; // save user to variable before the Joey's Site session is closed
session_write_close (); // close Joey's Site session to allow SimpleSAMLphp session to open
session_set_save_handler ( new SessionHandler (), true ); // stop using SessionHandlerJoey and use default PHP handler for SimpleSAMLphp
$samlObj = new SimpleSAML\Module\joeysiteauth\Auth\Source\JoeySiteAuth ( array (), array () );
$samlObj->login ( $user, function () { return;} ); // use custom authentication module to set atttributes and everything SimpleSAMLphp needs in the auth session/cookie
$session = \SimpleSAML\Session::getSessionFromRequest ();
$session->cleanup (); // must call this function when we are done with SimpleSAMLphp session and intend to use our Joey's Site session again
session_write_close ();
$_SESSION = $joeySiteSession; // restore Joey's Site session
header ( "Location: {$TRAINING_URL}" );
}

Migrate an existing project to Firebase : Authentication handling & Preserve existing JWT

I've an existing project that is built on :
AngularJS
Google App Engine backend, PHP, with an authentication based on JWT
I'm in process of rewriting the frontend to move to Angular 8 and I want to leverage the firebase features.
I'm currently working on integrating the authentication feature (username/password, google, twitter, facebook etc...)
And I'm thinking about my next step :
Once my use is authenticated with firebase, how can my GAE PHP backend check that the user is authenticated ?
In my JWT, I've set some basic user information, that are essential to my backend function.
uid, first name, last name, entityId, entityName, roleId, environmentId
I was imagining something like :
once authenticated with firebase, call my GAE Backend with the OAuth2 token
call some magic function that will validate the OAuth2 token and associate the firebase user, with my internal user table
reply with a JWT
Include the JWT and the OAuth2 token in every call
Would this work ? any suggestions ?
So here is how I did it :
On the client side (Angular Application), I use ngx-auth-firebaseui, to display the login form.
On the form, I set the call back that handle an authentication success:
login.component.html
<ngx-auth-firebaseui (onSuccess)="successfulLogin($event)"
(onError)="printError($event)">
</ngx-auth-firebaseui>
The code of the callback is here.
From the Firebase User object, I call the method getIdTokenResult() to get the firebase JWT.
And I then call my php backend via the authenticationService
login.component.ts
successfulLogin(user:User) {
console.log(user);
user.getIdTokenResult().then((idTokenResult:IdTokenResult)=> {
console.log(idTokenResult.token);
let token : string = idTokenResult.token;
let rcqJWTToken = this.authenticationService.authenticate( { token } as FirebaseJWT);
rcqJWTToken.subscribe((rcqToken:string)=> console.log("RCQ JWT Token : '"+rcqToken+"'"));
this.router.navigate['/welcome'];
});
}
Here I transmit the Firebase JWT to my php backend
authentication.service.ts
authenticate(firebaseJWTToken:FirebaseJWT):Observable<String>{
return this.http.post<String>(this.authenticationURL, firebaseJWTToken, httpOptions)
.pipe(
tap(_ => console.log('fetched RCQ JWT')),
catchError(this.handleError<String>('authenticate', ""))
);
}
On the server side :
I set the GOOGLE_APPLICATION_CREDENTIALS as an env var, like it is when deployed on Google App Engine
putenv("GOOGLE_APPLICATION_CREDENTIALS=/Users/myuser/.cred/GCP-project-ID.json");
I use Slimframework, so I instanciate the Firebase object in my dependencies.php file.
With the env var, Firebase do not need anything else.
check here : https://firebase-php.readthedocs.io/en/4.32.0/setup.html
use Kreait\Firebase;
use Kreait\Firebase\Factory;
/**
* Used to authenticate a firebase user, from it's Firebase JWT
* #property Firebase $firebase
* #param \Slim\Container $c
* #return Firebase
*/
$container['firebase'] = function (\Slim\Container $c)
{
$firebase = (new Factory)->create();
return $firebase;
};
and here comes the route where the authentication is done :
$app->post(getPrefix().'/firebase-authenticate', function($request, $response, $args) use ($app)
{
$token = $this->clientInputValidator->validateString("token" , $request->getParsedBodyParam("token" ), 1500 , true );
$username = "";
Logger::dataForLogging(new LoggingEntity(null, ["username"=>$username]));
try
{
$verifiedIdToken = $this->firebase->getAuth()->verifyIdToken($token);
}
catch (InvalidToken $e)
{
$response401 = $response->withStatus(401);
$response401->getBody()->write(json_encode(["error" =>"Authentication error"]));
$this->logger->error("Firebase authentication error", array('username' => $username, 'token' => $token));
return $response401;
}
$uid = $verifiedIdToken->getClaim('sub');
$user = $this->firebase->getAuth()->getUser($uid);
$this->logger->debug("Firebase JWT checked successfully", array('uid' => $uid,'user' => $user));
});
The main thing is here :
$verifiedIdToken = $this->firebase->getAuth()->verifyIdToken($token);
And the user details are retrieved here:
$user = $this->firebase->getAuth()->getUser($uid);
I can get the uid, email, and all the info in the Firebase JWT.
the token itself has a TTL of 1 hour, so I'll probably have to refresh the token and revalidate it against my backend.

Slim framework - API Security

So I have a RESTful API, but I want to be safe so that not everyone can do anything.
$app->get('/users' , function(Request $request, Response $response){
$sql = "SELECT * FROM users";
try{
// Get db object
$db = new db();
// Connect
$db = $db->connect();
$stmt = $db->query($sql);
$users = $stmt->fetchAll(PDO::FETCH_OBJ);
$db = null;
echo json_encode($users);
} catch(PDOException $e){
echo '{"error": {"text": '.$e->getMessage().'}}';
}
});
So when i go to http://localhost/API/users i get all users into a json table.
Inside my database my data are stored like [{"id":"1","username":"werknemer","password":"...","level":"1","name":"piet","surname":"jan","email":"pietjan#gmail.nl"}]
I would like everyone to see his own table through my API and if you are level 5.
Is there a solution for that?
Your example is pretty basic and it's a starting point for using some "auth" concept in your REST APIs.
First things first: Authentication != Authorization.
Split these two concepts, the first one is going to make a user registered and logged into your app, the second one makes the "hard work" that you are looking for in this example, so check if a specific user is able to do some stuff.
For authentication, you can provide all the methods that you want, but remember that in REST your app MUST be stateless and you should provide a token (passed via HTTP Headers) that will be used by your application for understanding if the user is LOGGED and CAN do some stuff.
That's the key concept: A token (see JWT or OAUTH) should be used for authorization, and a very basic authorization is: "USER LOGGED".
In your example, you should use the middlewares for filter the http request, and don't enter into the router callback if the user is not authorized (logged in || have not a minLevel:5).
Checkout JWT or OAuth2 for this kinda stuff for more info.
Check this out -> (https://github.com/damianopetrungaro/slim-boilerplate) for a basic example of JWT generation in a slim app (if you are going to use this boilerplate PLEASE do not use the md5 for hash password this is a pretty basic example)
You need to add authentication and then authorisation to your API.
Authentication is the process of knowing who is accessing the API. A good way to do this is to you OAuth 2. I like and use Brent Shaffer's OAuth 2.0 Server library. https://github.com/akrabat/slim-bookshelf-api/tree/master/api contains an implementation of an API that using OAuth 2 to authorise users.
Once you know who is logging in, you then need to limit their access based on their role (or level). This is called access control. I like the zend components for this. Try zend-permissions-rbac - there's a good article on how to use it on the ZF blog.

Login Using Api

I am working on a project and currently writing the backend. I have decided to implement it as a rest api since i need to write a web app as well as a mobile app. I am having problem understanding how do i login the user since rest api are stateless. I have read some material which mention basic authentication (sending login credentials) with each request or Oauth2.0. Basic authentication is not recommended and i don't understand why i should use Oauth2.0 because no third party will be using my api. My question is how should i implement login functionality and what are the standards ?
add passport package to your project ,see this for more info https://laravel.com/docs/5.4/passport
create password grant client
create new user with token
use retrofit or another package to call Laravel api
/* prepare httpClient */
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request orginal = chain.request();
Request.Builder builder = orginal.newBuilder();
builder.addHeader("Accept", "application/json");
if (tools.isAuthorized()) {
builder.addHeader("Authorization", "Bearer " + tools.getAccessToken());
}
builder.method(orginal.method(), orginal.body());
Request build = builder.build();
return chain.proceed(build);
}});
5- call api and get response then save user token.
You'll need to add a unique api_token column for your Users table.
$table->string('api_token', 60)->unique();
In Laravel 5.4, api.php holds the API routes, you'll need to use an out-of-the-box middleware auth:api, so you can authenticate requests by api_token.
Read more
http://bootstrapdojo.com/rest-api-laravel-5-4-with-token-authentication/
I guess you can create a REST API that offers CRUD operations on JSON web tokens.

Categories