Share OAuth2 Authentication Across Laravel Projects

Share OAuth2 Authentication Across Laravel Projects

This Laravel Authentication tutorial explains how to authenticate users using OAuth 2 and share the authentication across our Laravel apps. OAuth2 authentication across Laravel projects

Introduction

A question popping up every now and then is how to let users log in to separate (child) applications using a single account they own on a central application.

In this post I try to explain how to achieve this infrastructure, by creating a central application using Laravel Passport (example.com) where users register once and then use OAuth2 to grant access to their account to the other applications (app1.example.com, app2.example.com, etc.) using Laravel Socialite.

Note: the applications do not necessarily have to use the same domain.

In this way, users will be able to login to the child applications without creating a new, separate account.


Authorization request (Laravel Passport)

If you're not yet familiar with the OAuth2 protocol, I've included a section on the how and what below.

# What is OAuth2?

Before jumping into Laravel Passport, it is important to understand the OAuth protocol it implements.

OAuth is an open standard, designed to provide API access delegation. Think of using a third party Twitter app which can tweet on your behalf to the Twitter platform. I explicitly mention Twitter since development of this standard was (amongst others) driven by lead developer Blane Cook, in need of authorization of external parties.

After initial release of version 1.0 in 2010, the protocol matured over the course of 2 years after which version 2.0 was released. This improved protocol offers support for Bearer tokens and provides "specific authorization flows for web applications, desktop applications, mobile phones, and smart devices." (Wikipedia)

Let's have a look at what is meant with the terms "Bearer token" and "authorization flow" .

Bearer Tokens

The word "Bearer" means you're in possession of a certain (access) token. The bearer being the third party application, which possesses an access token issued by the identity provider. This token provides immediate access to a resource, without requiring a username and password. All necessary information is linked to this token, including user details and the scope of possible actions this third party may perform on behalf of this user.

The bearer token is usually included in the Headers of a GET or POST request to an API endpoint. A concrete example of using the Bearer token is shown below, making a GET request to '/api/user' using the Guzzle HTTP library to a corresponding API endpoint sending along a Bearer Authorization header.

$response = $client->request('GET', '/api/user', [
    'headers' => [
        'Accept' => 'application/json',
        'Authorization' => 'Bearer ' . $accessToken,
    ],
]);

Authorization Flow

Now that we know what a Bearer access token is ... how do we obtain one?

First, let's look at the formal roles in OAuth2:

  • - Resource Owner: the user who wants to login and delegate access to their account details to a third party application
  • - Resource Server: the server (API) where the user has an account
  • - Client: the third party application that wants to access the account information on the resource server

The OAuth 2.0 protocol performs a standard communication flow between Client and Resource Server, where each step and given/required parameters are defined in advance. Ultimately, the Client receives the Bearer access token from the Resource server. This process is illustrated in the figure below (created using draw.io).

Note: it is assumed that the Client (third party application) is registered with the Resource Server.


The OAuth authorization flow

After this little "dance", the Client now possesses an access token which can either be long-lived or short-lived (more secure). If the access token is short-lived, the Resource Server will also provide a refresh token which can be used - in combination with the client secret - to obtain a new access token in a way resembling Step 3 in the diagram above. Note: the "state" parameter is used to prevent CSRF related attacks, by verifying the request is not forged.

Now, let's see how Laravel Passport implements this protocol.

Laravel Passport

In case of our example, we want the identity provider (example.com) to make use of Laravel Passport.

Laravel Passport is an OAuth2 server, built upon the League OAuth2 server. It provides an easy implementation for existing Laravel applications by requiring the composer package.

Setting up the Resource Server

Follow the clear installation instructions and/or watch this explanation on Laracasts by Taylor Otwell. Don't forget to add the hasApiTokens trait to your User model.

After installation, you now have the possiblity to add new Clients having a callback url and an automatically generated secret. For each of the "child" applications (app1.example.com, app2.example.com, etc.) you need to create a new Client with their own callback, for now you might choose https://app1.example.com/login/callback for example (we will come back to this).


Creating a new Client in Laravel Passport

Laravel Passport will take care of the authorization dialog, providing an authorization code, verifying the client secret in combination with the authorization code and lastly provide a User object and (by default) a long-lived access token. The lifespan of the access and refresh tokens are configurable.


Example of a Client having an ID and a secret (in Laravel Passport)

# Laravel Socialite

Now that we have set-up the Resource Server (identity provider), we need to take care of the Client side of things.

Besides Passport, Laravel offers a package called Laravel Socialite, which will take care of the Client side of things when authenticating via OAuth2.

Out of the box, it allows authentication with the services of Facebook, Twitter, LinkedIn, Google, GitHub, GitLab and Bitbucket.

Socialite Providers

However, there is a collection of additional providers, amongst which an adapter supporting Laravel Passport.

Setting up the Client

To achieve a shared login system across multiple Laravel applications, my proposed solution involves making use of the Socialite provider for Laravel Passport.

Looking at the installation instructions of the Socialite provider, there are quite a number of steps, to make sure a client application is able to identify users using the Laravel Passport identity provider.

  1. Install Laravel Socialite
  2. Install Laravel Passport provider for Socialite
  3. Add methods to LoginController
  4. Copy the 'laravelpassport' config to config/services.php
  5. Add 'SocialiteWasCalled' event and listener to EventServiceProvider

Now, this seems like a lot of steps to repeat for every Client application we might already have and want to couple to our Resource Server. That's why I made a package (see GitHub repository) that combines Laravel Socialite with the Passport driver and can be configured in a more efficient way.

Socialite-Passport package

In a Client Laravel application that you want to couple to the Passport Resource Server, first install the custom 'socialite-passport' package:

composer require jhnbrn90/socialite-passport

Next, publish the configuration file:

php artisan vendor:publish --provider="JhnBrn90\SocialitePassport\SocialitePassportServiceProvider" --tag="config"

This will publish a file socialite-passport.php in the config directory, in which you can define which Controller (defaults to LoginController that ships with Laravel) and which method should be called when the login route (also configurable) is called.

return [
    'controller' => [
        'class' => \App\Http\Controllers\Auth\LoginController::class,
        'method' => 'loginWithPassport',
    ],

    'route' => [
        'name' => 'login',
        'uri' => '/login',
    ],
];

Assuming the defaults in the above configuration, after Authorization is granted by the Resource Owner at the Resource Server (Laravel Passport), the method loginWithPassport() will be called and a User object will be injected. This method needs to be implemented in the defined controller (in our example, the LoginController):

class LoginController extends Controller 
{
    public function loginWithPassport($user) 
    {
        // example:
        User::firstOrCreate(['name' => $user['name'], 'email' => ...]);
    }
}

To be able to provide the Resource Server with a client_id, callback_uri, etc. you must add these variables to your .env file:

LARAVELPASSPORT_CLIENT_ID=
LARAVELPASSPORT_CLIENT_SECRET=
LARAVELPASSPORT_REDIRECT_URI=/login/callback
LARAVELPASSPORT_HOST=https://example.com

Remember, the LARAVELPASSPORT_CLIENT_ID and LARAVELPASSPORT_CLIENT_SECRET come from the Laravel Passport Resource Server, where the Client needs to be created first.

The package will make sure to match the route you provide as the LARAVELPASSPORT_REDIRECT_URI environment variable and proxies the request through the corresponding method and controller you've configured in the config file.

Final remarks

That is all it takes to implement a basic functional shared login system.

I hope this post could shed some light on the OAuth2 protocol and how it can be used with the mentioned tools to achieve a shared authentication system amongst different Laravel projects.

It might be a good idea to also store the access token (and the resource token) on the user model to be able to update information whenever a user changes its profile on the Resource Server. Or to collect other data of that user from the Resource Server of course.

Summary
    • Laravel Passport implements a fully functional OAuth2 server (Resource Server)
    • Clients can be prepared using this package combining both Socialite with the Passport adapter

Clear Cache in Laravel 6.8 App using Artisan Command Interface (CLI)

Clear Cache in Laravel 6.8 App using Artisan Command Interface (CLI)

In Laravel 6 tutorial, we learn how to use PHP artisan command interface (CLI) to clear the cache from Laravel 6.8 application. How To Clear Cache in Laravel 6.8 Application using Artisan Command Line Interface (CLI)? How to clear route cache using php artisan command? How to easily clear cache in Laravel application? How to clear config cache in PHP Laravel via artisan command? How to clear Laravel view cache? How to Reoptimized class in Laravel via artisan CLI?

Today in this tutorial, we are going to learn how to clear route cache, laravel application cache, config cache, view cache and reoptimized class in a Laravel 6.8 application using artisan command-line interface.

I’m pretty sure many of you may have found yourself gotten into the situation where you do not see changes in the view after making the changes in the app.

Laravel application serves the cached data so caching problem occurs due to the robust cache mechanism of Laravel.

But, if you are still facing this issue, then you do not have to worry further. Let me do the honour of introducing you some of the best artisan commands to remove the cache from your Laravel app via PHP artisan command line interface.

Artisan is the command-line interface included with Laravel. It provides a number of helpful commands that can assist you while you build your application.

Table of Contents

  • Clear Route Cache in Laravel
  • Clear Laravel Application Cache
  • Clear Config Cache via PHP Artisan
  • Clear Laravel View Cache
  • Reoptimized Class
Clear Route Cache in Laravel

Laravel caching system also takes routes in consideration, to remove route cache in Laravel use the given below command:

php artisan route:cache
Clear Application Cache in Laravel

Run the following command to clear application cache:

php artisan cache:clear
Clear Config Cache in Laravel

Run the following command to clear config cache:

php artisan config:cache
Clear View Cache in Laravel

Run the following command to clean your view cache:

php artisan view:clear
Reoptimize Class

Run the below command to reoptimize the class loader:

php artisan optimize

Conclusion

We have completed this Laravel 6 tutorial, In this tutorial we learned how to use php artisan command to clear the cache from your Laravel application. We have answered the following questions in this article.

  • How to clear route cache using php artisan command?
  • How to easily clear cache in Laravel application?
  • How to clear config cache in PHP Laravel via artisan command?
  • How to clear Laravel view cache?
  • How to Reoptimized class in Laravel via artisan CLI?

Now, it’s your time to let me know what do you think about this laravel 6 article. Go forth and try these super awesome artisan commands and let me know how these commands are helping you.

Get Weather Data with Laravel Weather

Get Weather Data with Laravel Weather

Get Weather Data with Laravel Weather. Laravel Weather is a good package which we can use to get weather data. It's a wrapper around Open Weather Map API (Current weather). A wrapper around Open Weather Map API (Current weather)

🌤️ A wrapper around Open Weather Map API (Current weather)

Installation

You can install the package via composer:

source-shell
composer require gnahotelsolutions/laravel-weather
Usage
text-html-php
$weather = new Weather();

// Checking weather by city name
$currentWeatherInGirona = $weather->get('girona,es');

// You can use the city id, this will get you unambiguous results
$currentWeatherInGirona = $weather->find('3121456');

Units

By default the package uses metric for Celsius temperature results, this can be modified using the configuration file or on the fly:

text-html-php
$weather = new Weather();

$currentWeatherInGirona = $weather->inUnits('imperial')->get('girona,es');

Language

By default the package uses es for the description translation, this can be modified using the configuration file or on the fly:

text-html-php
$weather = new Weather();

$currentWeatherInGirona = $weather->inLanguage('en')->get('girona');

Guzzle Client Instance

If you need to use another instance of Guzzle, to modify headers for example:

text-html-php
$weather = new Weather();

$guzzle = $this->getSpecialGuzzleClient();

$currentWeatherInGirona = $weather->using($guzzle)->get('girona');

Testing

source-shell
composer test

PHP Security Triathlon: Filtering, Validation, and Escaping (Part 1)

PHP Security Triathlon: Filtering, Validation, and Escaping (Part 1)

Filtering input means escaping or removing unsafe characters. Before the data reaches the storage layer of the application, it is important to filter the input data. This is the first line of defense.

When developing applications, we generally have a convention: Don't trust any data from data sources that are not under our control. For example these external sources:

  • $_GET
  • $_POST
  • $_REQUEST
  • $_COOKIE
  • $argv
  • php://stdin
  • php://input
  • file_get_contents()
  • Remote database
  • Remote API
  • Data from the client

All of these external sources may be attack vectors and may (intentionally or unintentionally) inject malicious data into PHP scripts. It's easy to write a PHP script that accepts user input and then renders the output, but it takes some work to implement it securely. Here I use Chen Bianjin's three-board axe as an introduction, and I will introduce three techniques: filtering input, verifying data, and escaping output.

1. Filter input

Filtering input means escaping or removing unsafe characters. Before the data reaches the storage layer of the application, it is important to filter the input data. This is the first line of defense. If the site's comment box accepts HTML, users can add malicious comments <script> label, as follows:

<p>
This article is useful!
</p>
<script>windows.location.href="https://morioh.com";</script>

If this comment is not filtered, the malicious code will be stored in the database and then rendered in the web page. When the user visits this page, they will be redirected to a potentially unsafe phishing website (this attack has a more professional name: XSS attack). This simple example illustrates why we want to filter input data that we do not control. Usually the input data we want to filter includes HTML, SQL queries and user information.

HTML

We can use the htmlentities function provided by PHP to filter HTML. This function will convert all HTML tag characters (&, <,>, etc.) into corresponding HTML entities for safe rendering after the application storage layer is taken out. But sometimes we allow users to enter certain HTML elements, especially when entering rich text, such as pictures and links. However, we htmlentities cannot verify the HTML and cannot detect the character set of the input string, so we cannot achieve such a function.

<?php
$input = "<p><script>alert('Morioh.com');</script></p>";
echo htmlentities($input, ENT_QUOTES, 'UTF-8');

htmlentities The first parameter represents the HTML string to be processed, the second parameter represents the single quotes to be escaped, and the third parameter represents the character set encoding of the input string.

The htmlentities opposite is the html_entity_decode method, which converts all HTML entities into corresponding HTML tags.

In addition, PHP also provides a similar built-in function htmlspecialchars, which is also used to convert HTML tag characters to HTML entities, but only limited characters can be converted (refer to the official document: https://www.php.net/manual/en/function.htmlspecialchars.php ), if you want to convert all characters or use the htmlentities method, it is worth mentioning htmlentities that htmlspecialchars there is a method, as well as the opposite htmlspecialchars_decode.

If you want to directly remove all HTML tags from the input string, you can use the strip_tags method.

If you need more powerful filtering of HTML functions, you can use the HTML Purifier library, which is a very robust and secure PHP library that is designed to filter HTML input using specified rules. In Laravel we can use the corresponding extension package to implement the filtering function.

SQL query

Sometimes applications must construct SQL queries based on input data. These data may come from the query string of the HTTP request or from the URI fragment of the HTTP request. If you are not careful, it may be used by malicious people for SQL injection attacks (Splicing SQL statements to destroy the database or obtain sensitive information). Many junior programmers might write code like this:

$sql = sprintf(
    'UPDATE users SET password = "%s" WHERE id = %s',
    $_POST['password'],
    $_GET['id']
);

This is very risky. For example, someone sends a request to HTTP by:

POST /user?id=1 HTTP/1.1
Content-Length: 17
Content-Type: application/x-www-form-urlencoded

password=abc”;--

This HTTP request will set the password of each user to abc, because many SQL databases treat-as the beginning of a comment, subsequent text is ignored.

Unfiltered input data must not be used in SQL queries. If you want to use input data in SQL queries, you must use PDO prepared statements (PDO is a built-in database abstraction layer in PHP that provides a unified interface for different database drivers). PDO prepared statements are a function provided by PDO, which can be used to filter external data, and then embed the filtered data into SQL statements to avoid the above SQL injection problems. In addition, the prepared statements can be compiled and run multiple times to reduce the impact on the system Occupy resources for higher execution efficiency. After PDO, we will focus on the database part later.

It is worth noting that many modern PHP frameworks use the MVC architecture pattern to encapsulate database operations into the Model layer. The bottom layer of the framework has already done a good job of avoiding SQL injection, as long as we use the methods provided by the model class to perform database operations You can basically avoid the risk of SQL injection.

Let's take Laravel as an example to see how the bottom layer avoids SQL injection. Rewrite the above updatestatement, the code will be like this:

$id = $_GET['id'];
$password = $_POST['password'];
User::find($id)->update(['password'=>bcrypt($password)]);

Because the model class calls the builder method at the bottom, the builder (Illuminate\Database\Query\Builder) updatemethod will eventually be called:

public function update(array $values)
{
    $bindings = array_values(array_merge($values, $this->getBindings()));

    $sql = $this->grammar->compileUpdate($this, $values);

    return $this->connection->update($sql, $this->cleanBindings($bindings));
}

This code passes in the parameter to be updated, and then $binding sobtains the binding relationship. Here we get an array containing the password sum updated_at(default update timestamp), and then generate the preprocessing through Illuminate\Database\Query\Grammars\Grammar the compileUpdate method of the Grammar() class. SQL statement, the corresponding SQL statement here is:

update `users` set `password` = ?, `updated_at` = ? where `id` = ?

Then finally pass the prepared SQL statement and the corresponding binding relationship to the database for execution. We will continue to discuss about SQL injection in the subsequent database section.

User profile information

If there is a user account in the application, it may be necessary to process profile information such as email addresses, phone numbers, postal codes, and so on. PHP expected this to happen, so it provided a filter_var sum filter_input function. The parameters of these two functions can use different flags to filter different types of input: email addresses, URL-encoded strings, integers, floating-point numbers, HTML characters, URLs, and specific ranges of ASCII characters.

The following example shows how to filter email addresses and remove !#$%&'*+-/=?^_{|}[email protected][]all characters except letters, numbers, and:

<?php
$email = '[email protected]';
$emailSafe = filter_var($email, FILTER_SANITIZE_EMAIL);

For more filter_var use, please refer to the official PHP documentation: https://www.php.net/manual/en/function.filter-var.php , and the corresponding removal filter please refer to: https://www.php.net/manual/en/filter.filters.sanitize.php.

Of course, filter_var functions can also be used to filter other form submission data.