libaro / miqey-client
A package to integrate MiQey into your application
Requires
- php: ^8.0|^8.1|^8.2|^8.3
- ext-json: *
- jenssegers/agent: ^2.6
Requires (Dev)
- orchestra/testbench: ^6.0
- pestphp/pest: ^1.23
- pestphp/pest-plugin-laravel: ^1.4
- phpstan/phpstan: ^1.8
- phpunit/phpunit: ^9.0
README
The MiQey Client Laravel Package simplifies the integration of the MiQey functionality into your Laravel projects. MiQey is designed to facilitate a secure login procedure by generating sign requests, managing user responses through QR codes or SMS, and seamlessly logging users into your projects.
Installation
You can install the package via composer:
composer require digitalhq-labs/miqey-client
Publish the config file:
php artisan vendor:publish --provider="DigitalHQ\MiQey\MiQeyServiceProvider" --tag="config"
Usage
The miQey-client package takes care of sending communicating with the miQey-service to handle user authentication. The first thing you should do is add this code to your login page:
const pusherKey = 'your pusher key'; const subChannel = 'signRequest_{generated_code_from_MiQey}'; const authEndpoint = '/miqey/validate'; var pusher = new Pusher(pusherKey, { cluster: 'eu' }); var channel = pusher.subscribe(subChannel); channel.bind('sign-request-received', function (data) { window.location.href = authEndpoint + '?token=' + data.token });
The miQey-client will emit a socket event when a 'User Authenticated'-webhook callback is made to your application, which will be picked up by the listener in this script. Upon receiving the socket event, your login page will invoke the ValidationController of the miQey-client, which will log in your user based on their phone number. The phone number is received from the server cache using the token passed in the socket event.
With this single code insert, you can harness the full power of miQey!
Generated Files
Underneath, you will find a breakdown of the vendor files generated by the package and their role in the miQey integration into your project.
config/miqey.php
Installing miQey-client generates a config file to store the credentials and endpoints needed to communicate with the miQey authentication service. It contains the following keys that should point to the correct values in your environment-file:
-
api_key: Your personal API-key to integrate the service into your project. You can get your API-key from settings page of your corresponding application on the miQey platform (https://secureid.digitalhq.com/app/{application_id}/settings).
-
webhook_secret: The miQey client uses this value to decrypt the webhook signature that is included in the 'authenticated'-callback. This safeguards the communication between miQey and your application from man-in-the-middle attacks. You can generate your webhook secret on the settings page of your corresponding application on the miQey platform (https://secureid.digitalhq.com/app/{application_id}/settings).
-
webhook_endpoint: The endpoint to which miQey has to send the 'authenticated'-webhook callback. The URL can be set on the settings page of your corresponding application on the miQey platform (https://secureid.digitalhq.com/app/{application_id}/settings). Ideally, the URL should have the following structure: {your_application_base_url}/api/sign/webhook
-
user_model: The path to the User-model in your project. The miQey-client uses this model to authenticate the user in your application, so you don't have to authenticate the users yourself anymore. It defaults to '\App\Models\User'.
return [ 'api_key' => env('MIQEY_API_KEY'), 'webhook_secret' => env('MIQEY_WEBHOOK_SECRET'), 'webhook_endpoint' => env('MIQEY_WEBHOOK_ENDPOINT'), 'user_model' => env('MIQEY_USER_MODEL', '\App\Models\User'), ];
routes/routes.php
miQey adds two new routes to your project so that the backend-service can reach your application:
-
POST {webhook_endpoint}, default /miqey/webhook: This is the endpoint that miQey sends the webhook to when a sign request is signed in the miQey-backend. You can change this webhook on the settings page of the corresponding application in the miQey portal.
-
GET /miqey/validate: When your frontent receives the socket event from the backend that a user is authenticated, it will redirect to this endpoint, which will invoke the ValidationController to log the user into your application.
src/Events/SignSmsRequestReceived
The SignSmsRequestReceived is the socket event that is emitted when the miQey client receives the 'User Authenticated'-callback from the miQey-backend service. It passes a token, which can be used to retrieve the user's phone number from the cache in your backend.
class SignSmsRequestReceived implements ShouldBroadcastNow { use Dispatchable; use InteractsWithSockets; use SerializesModels; public function __construct( public string $code, public string $token ) { } /** * Get the channels the event should broadcast on. * * @return array */ public function broadcastOn(): array { return ["signRequest_$this->code"]; } public function broadCastAs(): string { return 'sign-request-received'; } }
src/Handlers/WebhookHandler.php
The WebhookHandler is invoked when a 'User Authenticated'-callback is sent to the webhook_endpoint of your application. Firstly, it validates the webhook's authenticity by decrypting the signature in the header using the webhook secret in the miQey-config file. When the origin is verified, it takes the user's phone number and the sign code from the callback body. The phone_number is put in cache as to not expose it to the frontend of your application. A new token is made as a cache-key. This token, together with the sign code, is passed with the socket event described just above this paragraph, which is fired at that point to let your loginpage know to redirect.
class WebhookHandler { public function __invoke(WebhookReceivedRequest $request) { $data = $request->validated(); $signature = $request->header('Signature'); $calculatedSignature = hash_hmac('sha256', $request->getContent(), config('miqey-login.webhook_secret')); if (! hash_equals($signature, $calculatedSignature)) { return response()->json(status: 401); } $phone = $data['phone']; $code = $data['code']; $token = Str::random(32); Cache::put($token, $phone, now()->addSeconds(10)); event(new SignSmsRequestReceived($code, $token)); $now = now(); for($i = 0; $i < 10; $i++) { dispatch(function() use ($token, $code) { event(new SignSmsRequestReceived($code, $token)); })->delay($now->addSeconds($i+1)); } return response()->json(status: 204); } }
src/Http/Controllers/ValidationController.php
The ValidationController is the last stop in the miQey-authentication process. When this controller is invoked it gets the phone number of the user that was authenticated from the server cache. It then resolves your user model using the path in your miQey config file, and queries that model to get the user by phone_number. Lastly, it log the user into your app, and redirect to your authenticated root url.
class ValidationController extends Controller { public function __invoke(Request $request) { $hasToken = Cache::has($request->get('token')); if (! $hasToken) { // todo: change to exception? abort(403, 'token mismatch'); } $phoneNumber = Cache::pull($request->get('token')); $userClass = config('miqey.user_model'); $user = $userClass::query() ->where('phone_number', '=', $phoneNumber) ->first(); if (is_null($user)) { // todo: change to exception? abort(403, 'user not found'); } Auth::login($user); return redirect()->to(App\Providers\RouteServiceProvider\RouteServiceProvider::HOME); } }
Testing
composer test
Changelog
Please see CHANGELOG for more information what has changed recently.
Security
If you discover any security related issues, please email github@libaro.be instead of using the issue tracker.
License
The MIT License (MIT). Please see License File for more information.