Secure Your Messages With Laravel Encryption
Encryption is the best tool in this fight for your right to privacy. Now more than anytime in history privacy is of the essence. In today’s tutorial I will show you how to create a simple messenger service in Laravel and Vue.js; however they will be password protected and encrypted therefore the receiver must know the password beforehand to read the message. All code can be found on my Github repo.
Setting Up The Backend
The application itself has only one model and that is the Message model. It has 3 properties: content, email and passphrase. The content stores the encrypted message. The email is the email address of who is receiving the message. Finally the passphrase is the password that protects the message from being opened by anybody.
php artisan make:model -m Message
Open the Message.php file and make it Notifiable and change the $fillable
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\Model;
class Message extends Model
{
use Notifiable;
//
public $fillable = ['content','passphrase','email'];
}
Next open up the migration file that was created with the model and add the following:
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateMessagesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('messages', function (Blueprint $table) {
$table->increments('id');
$table->longText('content');
$table->string('passphrase');
$table->string('email');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('messages');
}
}
Next create the controller. This controller will be RESTful with an extra method for decrypting the messages.
php artisan make:controller --resource MessageController
Open the file and fill in the methods store(), show() and decrypt()
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Message;
use App\Notifications\MessageCreated;
use Illuminate\Contracts\Encryption\DecryptException;
class MessageController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
//
$message = Message::Create([
'content' => encrypt($request->content),
'passphrase' => encrypt($request->password),
'email' => $request->email
]);
$message->notify(new MessageCreated($message));
return response()->json($message);
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
//
$message = Message::findOrFail($id);
return view('message')->with([
'message' => $message
]);
}
public function decrypt(Request $request, $id){
try{
$message = Message::findOrFail($id);
if($request->password == decrypt($message->passphrase)){
$message = decrypt($message->content);
$with = [
'message' => $message
];
return response()->json($with);
}
}
catch (DecryptException $e){
return response()->json($e);
}
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
//
}
}
As you can see there is a call to a notification that has not been created yet so create it
php artisan make:notification MessageCreated
Open that up and replace it with the following which will call the toMail method and alert the recipient they have a new message to view
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
class MessageCreated extends Notification
{
use Queueable;
public $message;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct(\App\Message $message)
{
//
$this->message = $message;
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->line('You have a new encrypted message.')
->line('You should have been given the passphrase')
->action('Decrypt and Read Now!', url('/message/'.$this->message->id))
->line('Thank you for using our application!');
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}
Lastly the api and web routes need to be updated
In web.php
<?php
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('/', function () {
return view('home');
});
Route::get('/home', 'HomeController@index')->name('home');
Route::get('/message/{id}','MessageController@show');
In api.php
<?php
use Illuminate\Http\Request;
/* |-------------------------------------------------------------------------- | API Routes Fr */
Route::middleware('auth:api')->get('/user', function (Request $request) { return $request->user(); });
Route::resource('/message','MessageController');
Route::post('/decrypt-message/{id}','MessageController@decrypt');
Lastly let’s create the view files. As a shortcut I scaffold authentication even though we aren’t using it to get the bootstrap layouts and the home.blade.php file. Copy the home.blade.php file to another file called message.blade.php.
In home.blade.php
@extends('layouts.app')
@section('content')
<send-message></send-message>
@endsection
In message.blade.php
@extends('layouts.app')
@section('content')
<read-message v-bind:message="{{$message}}"></read-message>
@endsection
That’s it, the back end is complete!
Front End
The Vue.js side of things is pretty simple there are two components a MessageSend and a MessageRead. Create a file in your resources/assets/js/components folder called MessageSend.vue and MessageRead.vue.
In MessageSend.vue
<template>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card card-default">
<div class="card-header">Send Encrypted Message</div>
<div class="card-body">
<div class="form-group">
<input type="email" placeholder="Enter email address" class="form-control" v-model="message.email">
</div>
<div class="form-group">
<input type="password" placeholder="Enter passphrase" class="form-control" v-model="message.password">
</div>
<div class="form-group">
<textarea class="form-control" placeholder="Enter Message" v-model="message.content"></textarea>
</div>
<div class="form-group">
<button class="btn btn-sm btn-primary" v-on:click="send()">Send Message</button>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
mounted() {
console.log('Component mounted.')
},
data() {
return {
message: {}
}
},
methods: {
send: function(){
axios.post('/api/message',this.message).then(data =>{
console.log(data);
alert('Message Sent!');
}).catch(err => {
console.log(err);
})
}
}
}
</script>
In MessageRead.vue
<template>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card card-default">
<div class="card-header">Read Encrypted Message</div>
<div class="card-body">
<div class="form-group">
<input type="password" placeholder="Enter passphrase" class="form-control" v-model="password">
</div>
<div class="form-group">
<textarea class="form-control" placeholder="Enter Message" v-model="msg.content"></textarea>
</div>
<div class="form-group">
<button class="btn btn-sm btn-primary" v-on:click="decrypt()">Decrypt Message</button>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
mounted() {
console.log('Component mounted.')
},
data() {
return {
password: '',
msg : this.message
}
},
props: ['message'],
created(){
},
methods: {
decrypt: function(){
var that = this;
axios.post('/api/decrypt-message/'+this.message.id,{password:this.password}).then(data =>{
that.message.content = data.data.message;
}).catch(err => {
console.log(err);
})
}
}
}
</script>
In your resources/assets/js/app.js file don’t forget to add your components
/**
* 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('send-message', require('./components/MessageSend.vue'));
Vue.component('read-message', require('./components/MessageRead.vue'));
const app = new Vue({
el: '#app'
});