OAuth in Tempest 2.2
Tempest 2.2 gets a new OAuth integration which makes authentication super simple
by Brent on October 02, 2025Authentication is a challenging problem to solve. It's not just about logging a user in and session management, it's also about allowing them to manage their profile, email confirmation and password reset flows, custom authentication forms, 2FA, and what not. Ever since the start of Tempest, we've tried a number of approaches to have a built-in authentication layer that ships with the framework, and every time the solution felt suboptimal.
There is one big shortcut when it comes to authentication, though: outsource it to others. In other words: OAuth. Everything account-related can be managed by providers like Google, Meta, Apple, Discord, Slack, Microsoft, etc. All the while the implementation on our side stays incredibly simple. With the newest Tempest 2.2 release, we've added a firm foundation for OAuth support, backed by the incredible work done by the PHP League. Here's how it works.
Tempest comes with support for many OAuth providers (thanks to the PHP League, again):
- GitHub
- Discord
- Microsoft
- Slack
- Apple
- Any other OAuth platform by using
GenericOAuthConfig
.
Whatever OAuth providers you want to support, it's as easy as making a config file for them like so:
use Tempest\Auth\OAuth\Config\GitHubOAuthConfig; return new GitHubOAuthConfig( tag: 'github', clientId: env('GITHUB_CLIENT_ID'), clientSecret: env('GITHUB_CLIENT_SECRET'), redirectTo: [GitHubAuthController::class, 'handleCallback'], scopes: ['user:email'], );
use Tempest\Auth\OAuth\Config\DiscordOAuthConfig; return new DiscordOAuthConfig( tag: 'discord', clientId: env('DISCORD_CLIENT_ID'), clientSecret: env('DISCORD_CLIENT_SECRET'), redirectTo: [DiscordAuthController::class, 'callback'], );
Now we're ready to go. Generating a login link can be done by using the OAuthClient
interface:
namespace App\Auth; use Tempest\Auth\OAuth\OAuthClient; use Tempest\Container\Tag; use Tempest\Router\Get; final readonly class DiscordAuthController { public function __construct( #[Tag('discord')] private OAuthClient $oauth, ) {} #[Get('/auth/discord')] public function redirect(): Redirect { return $this->oauth->createRedirect(); } // … }
Note how we're using tagged singletons to inject our OAuthClient
instance. These tags come from the provider-specific configurations, and you can have as many different OAuth clients as you'd like. Finally, after a user was redirected and has authenticated with the OAuth provider, they will end up in the callback action, where we can authenticate the user on our side:
namespace App\Auth; use Tempest\Auth\Authentication\Authenticatable; use Tempest\Auth\OAuth\OAuthClient; use Tempest\Auth\OAuth\OAuthUser; use Tempest\Container\Tag; use Tempest\Router\Get; final readonly class DiscordAuthController { public function __construct( #[Tag('discord')] private OAuthClient $oauth, ) {} #[Get('/auth/discord')] public function redirect(): Redirect { return $this->oauth->createRedirect(); } #[Get('/auth/discord/callback')] public function callback(Request $request): Redirect { $this->oauth->authenticate( $request, function (OAuthUser $user): Authenticatable { return query(User::class)->updateOrCreate([ 'email' => $user->email, ], [ 'discord_id' => $user->id, 'username' => $user->nickname, ]); } ) return new Redirect('/'); } }
As you can see, there's still a little bit of manual work involved within the OAuth callback action. That's because Tempest doesn't make any assumptions on how "users" are modeled within your project and thus you'll have to create or store those user credentials somewhere yourself. However, we also acknowledge that some kind of "default flow" would be useful for projects that just need a simple OAuth login with a range of providers. That's why we're now working on adding an OAuth installer: it will prompt you which providers to add in your project, prepare all config objects and controllers for you, and will assume you're using our built-in user integration.
All in all, I think this is a very solid base to build upon. You can read more about using Tempest's OAuth integration in the docs, and make sure to join our Discord if you want to stay in touch!