Skip to content
This repository was archived by the owner on Dec 2, 2021. It is now read-only.

Commit 18278dc

Browse files
committed
Docs on 2fa and API
1 parent 66f50bf commit 18278dc

File tree

1 file changed

+187
-0
lines changed

1 file changed

+187
-0
lines changed

Resources/doc/api.md

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
Two-Factor Authentication in an API
2+
===================================
3+
4+
This guide describes how to set-up two-factor authentication in a Symfony application that doesn't generate a frontend,
5+
but provides API endpoints instead.
6+
7+
## Prerequisites
8+
9+
To make two-factor authentication work in an API, your **API has to be stateful**. That means the API is starting a
10+
session which is passed by the client on every call. The session is necessary for two-factor authentication to store the
11+
state of the login - if the user has already completed two-factor authentication or not.
12+
13+
## Setup
14+
15+
ℹ️ For simplicity of this guide, it is assumed that you're building a JSON API and you're using the `json_login`
16+
authentication mechanism, which comes with Symfony. For any other authentication mechanism it should work the same or at
17+
least similar, as long as it lets you configure a custom success handler.
18+
19+
You need to implement 4 classes:
20+
21+
1) A custom success handler for the authentication mechanism
22+
2) A custom "two-factor authentication required" handler for the two-factor authentication
23+
3) A custom success handler for the two-factor authentication
24+
4) A custom failure handler for the two-factor authentication
25+
26+
### 1) Response on login
27+
28+
This first response is returned after the user logged in. Without two-factor authentication, it would either return
29+
a "login success" or "login failure" response. With two-factor authentication, you eventually need to return a third
30+
type of response to tell the client that authentication hasn't completed yet and two-factor authentication is required.
31+
The client should show the two-factor authentication form then.
32+
33+
If you provide multiple authentication mechanisms for the user to identify themselves, you have to do this for each one
34+
of them.
35+
36+
To implement such a response you need to a custom success handler:
37+
38+
```php
39+
<?php
40+
41+
namespace App\Security;
42+
43+
use Scheb\TwoFactorBundle\Security\Authentication\Token\TwoFactorTokenInterface;
44+
use Symfony\Component\HttpFoundation\Request;
45+
use Symfony\Component\HttpFoundation\Response;
46+
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
47+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
48+
49+
class AuthenticationSuccessHandler implements AuthenticationSuccessHandlerInterface
50+
{
51+
public function onAuthenticationSuccess(Request $request, TokenInterface $token): Response
52+
{
53+
if ($token instanceof TwoFactorTokenInterface) {
54+
// Return the response to tell the client two-factor authentication is required.
55+
return new Response('{"login": "success": "2fa_complete": false}');
56+
}
57+
58+
// Otherwise return the default response for successful login. You could do this by decorating
59+
// the original authentication success handler and calling it here.
60+
}
61+
}
62+
```
63+
64+
Register it as a service and configure it as a custom `success_handler` for the authentication method:
65+
66+
```yaml
67+
# app/config/security.yml
68+
security:
69+
firewalls:
70+
your_firewall_name:
71+
json_login: # The authentication mechanism you're using
72+
success_handler: your_api_success_handler
73+
```
74+
75+
### 2) Response to require two-factor authentication
76+
77+
Configure a response that is returned when the user requests a path, but it is not accessible (yet), because the user
78+
has to complete two-factor authentication first. This could be the same as your "access denied" response.
79+
80+
Create a class which implements `Scheb\TwoFactorBundle\Security\Http\Authentication\AuthenticationRequiredHandlerInterface`
81+
to return the response.
82+
83+
```php
84+
<?php
85+
86+
namespace App\Security;
87+
88+
use Scheb\TwoFactorBundle\Security\Http\Authentication\AuthenticationRequiredHandlerInterface;
89+
use Symfony\Component\HttpFoundation\Request;
90+
use Symfony\Component\HttpFoundation\Response;
91+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
92+
93+
class TwoFactorAuthenticationRequiredHandler implements AuthenticationRequiredHandlerInterface
94+
{
95+
public function onAuthenticationRequired(Request $request, TokenInterface $token): Response
96+
{
97+
// Return the response to tell the client that authentication hasn't completed yet and
98+
// two-factor authentication is required.
99+
return new Response('{"error": "access_denied", "2fa_complete": false}');
100+
}
101+
}
102+
```
103+
104+
Register it as a service and configure it as the `required_handler` of the `two_factor` authentication method:
105+
106+
```yaml
107+
# app/config/security.yml
108+
security:
109+
firewalls:
110+
your_firewall_name:
111+
two_factor:
112+
required_handler: your_api_2fa_required_handler
113+
```
114+
115+
### 3) Response when two-factor authentication was successful
116+
117+
Configure a response that is returned when two-factor authentication was completed successfully and the user is now
118+
fully authentication. Implement another success handler for it:
119+
120+
```php
121+
<?php
122+
123+
namespace App\Security;
124+
125+
use Symfony\Component\HttpFoundation\Request;
126+
use Symfony\Component\HttpFoundation\Response;
127+
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
128+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
129+
130+
class TwoFactorAuthenticationSuccessHandler implements AuthenticationSuccessHandlerInterface
131+
{
132+
public function onAuthenticationSuccess(Request $request, TokenInterface $token): Response
133+
{
134+
// Return the response to tell the client that authentication including two-factor
135+
// authentication is complete now.
136+
return new Response('{"login": "success", "2fa_complete": true}');
137+
}
138+
}
139+
```
140+
141+
Register it as a service and configure it as the `success_handler` of the `two_factor` authentication method:
142+
143+
```yaml
144+
# app/config/security.yml
145+
security:
146+
firewalls:
147+
your_firewall_name:
148+
two_factor:
149+
success_handler: your_api_2fa_success_handler
150+
```
151+
152+
### 4) Response when two-factor authentication failed
153+
154+
Configure a response that is returned when two-factor authentication was tried, but authentication failed for some
155+
reason. Implement a failure handler for it:
156+
157+
```php
158+
<?php
159+
160+
namespace App\Security;
161+
162+
use Symfony\Component\HttpFoundation\Request;
163+
use Symfony\Component\HttpFoundation\Response;
164+
use Symfony\Component\Security\Core\Exception\AuthenticationException;
165+
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
166+
167+
class TwoFactorAuthenticationSuccessHandler implements AuthenticationFailureHandlerInterface
168+
{
169+
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
170+
{
171+
// Return the response to tell the client that 2fa failed. You may want to add more details
172+
// from the $exception.
173+
return new Response('{"error": "2fa_failed", "2fa_complete": false}');
174+
}
175+
}
176+
```
177+
178+
Register it as a service and configure it as the `failure_handler` of the `two_factor` authentication method:
179+
180+
```yaml
181+
# app/config/security.yml
182+
security:
183+
firewalls:
184+
your_firewall_name:
185+
two_factor:
186+
failure_handler: your_api_2fa_failure_handler
187+
```

0 commit comments

Comments
 (0)