Discord OAuth2 Flow in Laravel
https://discordbrowser.com

Discord OAuth2 Flow in Laravel

#Discord #Laravel #Programming #WebDevelopment #PHP

So, you may be wondering how sites let you log in with your Discord profile. It can be a daunting and confusing process if you've never worked with OAuth before, or if you're not familiar with REST API's.


Discord Oauth2 Authorization

The first steps start in Discord itself. Head over to the Discord Developer Portal and create an application.


Create a new Discord Application

Inside the application you can new create your scopes and your redirect uri.

For now, just turn on the identify scope, and make sure, this is your redirect uri if you are running a local development server:

https://localhost/(your-prefix)/callback


create redirect uri

Now that you have your redirect uri, get your client secret and client id in the root of your application, do NOT share your client secret.


client id and client secret

At this point you may decide to use a pre-made package. As a contributor, I suggest using the Larascord Package.

If you decide not to use the package, then you may continue following along to learn how this package works under the hood.

After creating or opening your existing Laravel project, add the following to your .env:


env variables

Access these variables with a config file.


Client ID example

After assigning each of your new .env variables, be sure to clear your config cache.

php artisan config:clear        

Let's set up the route for the login.

The following url will look something like this:

Route::redirect('/login', 'https://discord.com/oauth2/authorize?client_id=' . config('config.client_id')
    . '&redirect_uri=' . config('config.redirect_uri')
    . '&response_type=code&scope=' . implode('%20', explode('&', config('config.scopes')))
    . '&prompt=' . config('config.prompt', 'none'))
    ->name('login');
         

When going to /login, your users will be redirected to the Discord OAuth login. This url that is built will look just like the url the Discord application provides for you.

Add the following routes as well:

Route::group(['prefix' => config('config.route_prefix', 'discordbrowser')], function() {
    Route::get('/callback', [DiscordController::class, 'handle'])
        ->name('discord.login');

    Route::redirect('/refresh-token', url('login'))
        ->name('discord.refresh_token');
});        

The following will trigger a 'handle' method in the DiscordController. This is where we will access the token, and use it to fetch the user's information.

The following 'handle' method will look something like this:

use App\Http\Requests\StoreUserRequest;

...

public function handle(StoreUserRequest $request): RedirectResponse | JsonResponse
{
  //Get access token from discord api
  try {
    $accessToken = (new DiscordService())->getAccessTokenFromCode($request->get('code'));
  } catch (\Exception $e) {
    return $this->throwError('invalid_code', $e);
  }
        

A custom exception can be created if you please. This also uses a DiscordService to fetch the AccessToken and assigns it to the $accessToken variable.

The DiscordService will now have to be created. We will start by creating a 'getAccessTokenFromCode' method within this file:

class DiscordService
{
    //Discord oauth service url
    protected string $tokenURL = 'https://discord.com/api/oauth2/token';

    //Discord api url
    protected string $baseApi = 'https://discord.com/api';

    //Discord Bot Token
    protected string $botToken;

    //Token request required data
    protected array $tokenData = [
        'client_id' => NULL,
        'client_secret' => NULL,
        'grant_type' => 'authorization_code',
        'code' => NULL,
        'redirect_uri' => NULL,
        'scope' => NULL,
    ];

    //User service constructor
    public function __construct()
    {
        $this->tokenData['client_id'] = config('config.client_id');
        $this->tokenData['client_secret'] = config('config.client_secret');
        $this->tokenData['redirect_uri'] = config('config.redirect_uri');
        $this->tokenData['scope'] = config('config.scopes');
        $this->botToken = config('config.bot_token');
    }

    //Handles oauth2 callback and returns access token
    public function getAccessTokenFromCode(string $code): AccessToken
    {
        $this->tokenData['code'] = $code;

        $response = Http::asForm()->post($this->tokenURL, $this->tokenData);

        $response->throw();

        return new AccessToken(json_decode($response->body()));
    }        

This class is constructing the tokenData based on the info in your .env, and assigning the 'code' property passed in from the 'handle' method in the DiscordController. When you dd(json_decode($response->body()), you will receive an object that looks something like this:


Access Token data received from Discord

Congratulations! You're almost there, you can see I created an AccessToken Type that stores the data received from the Discord API. This object is then linked to the $accessToken variable created in the DiscordController.

Time to fetch the user's data with the newly created AccessToken.

Back to the DiscordController within the 'handle' method. Add in this block of code:

// get user from discord api
        try {
            $user = (new DiscordService())->getCurrentUser($accessToken);
            $user->setAccessToken($accessToken);
        } catch (\Exception $e) {
            return $this->throwError('authorization_failed', $e);
        }        

This block fetches a user object from the Discord API using their access token. As you can see we use another method from the DiscordService called 'getCurrentUser'. Let's create that method now:

//Authenticate user with access token and return user data
    public function getCurrentUser(AccessToken $accessToken): UserType
    {
        $response = Http::withToken($accessToken->access_token)->get($this->baseApi . '/users/@me');

        $response->throw();

        return new UserType(json_decode($response->body()));
    }        

This method fetches the user data from the Discord API with their access token. To see the data fetched, you can dd(json_decode($response->body());

This will return an object that looks like this:


user data fetched from Discord API

As you can see, this data is also assigned to a user type, and this object is linked to the $user variable in the DiscordController.

Congratulations! You have successfully integrated Discord's OAuth2 functionality into your application!

You can now use this $user variable to store data in your db, display it, or create whatever flows you need!

Please check out Larascord and give it a star! Very awesome and well thought out package.

Also check out our site that uses many aspects of the Discord API: DiscordBrowser.

Here is a link to the Discord documentation to add more features to your application! Discord API Documentation.

If you have any questions, feel free to shoot a message! Here's a link to my personal Github.


要查看或添加评论,请登录

Damion Voshall的更多文章

  • LaraWoW Part 3 - Logging In The User

    LaraWoW Part 3 - Logging In The User

    Welcome back to part 3 of the WoW wrapper for Laravel. We're going to save the user's data to the db, and log them in.

  • LaraWoW Part 2 - Fetching The User

    LaraWoW Part 2 - Fetching The User

    Welcome to Part 2 of the LaraWoW development process! This time around, we're going to implement the functionality that…

  • LaraWoW Part 1 - Fetch Access Token

    LaraWoW Part 1 - Fetch Access Token

    Today, I am starting development on a Laravel wrapper for the battle.net API.

    3 条评论

社区洞察

其他会员也浏览了