JWT with Social Login (Google) #4147
Replies: 3 comments 2 replies
-
Authentication within allauth is inherently a stateful process. We need a session to store bits of information, in order to facilitate e.g. 2FA, OAuth handshake, and so on. Therefore, allauth requires a session, and as a result, either a session cookie or a header (X-Session-Token) pointing to the session. Now, once the user is authenticated, you are free to issue a different kind of token so that your own APIs are accessed in a manner that does not require a session. You can do this by implementing a token strategy and issuing e.g. a JWT token. See: https://docs.allauth.org/en/latest/headless/tokens.html Do note that you need to be careful here. Just because you are using JWT does not mean things are stateless. E.g. a logout should invalidate existing JWT tokens just as well. |
Beta Was this translation helpful? Give feedback.
-
This I am not following. If you use standard allauth.headless in app mode, you are also dealing with sessions. Not in the form of cookies, but in the form of an X-Session-Token header pointing to the session. The above suggests you have somehow customized things?
This flow: Does not work in 'app' mode -- see the explanation in the documentation. You will need to use https://docs.allauth.org/en/latest/headless/openapi-specification/#tag/Authentication:-Providers/paths/~1_allauth~1%7Bclient%7D~1v1~1auth~1provider~1token/post instead -- which does mean you are responsible yourself for obtaining an |
Beta Was this translation helpful? Give feedback.
-
@pennersr I ultimately got what I wanted; from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
from allauth.socialaccount.providers.google.views import GoogleOAuth2Adapter as DefaultGoogleOAuth2Adapter
# ...
class GoogleOAuth2Adapter(DefaultGoogleOAuth2Adapter):
def get_callback_url(self, request, app):
return httpkit.get_frontend_url(
request,
"socialaccount_callback",
).format(provider="google")
class AllAuthSocialAccountAdapter(DefaultSocialAccountAdapter):
def get_provider(self, request, provider, client_id=None):
provider_class = super().get_provider(request, provider, client_id)
if provider == "google":
provider_class.oauth2_adapter_class = GoogleOAuth2Adapter
return provider_class class OAuth2CallbackView(DefaultOAuth2CallbackView):
def dispatch(self, request, *args, **kwargs):
if not request.method == "POST":
return ErrorResponse(
request,
exception=AuthError.METHOD_NOT_ALLOWED,
)
provider = self.adapter.get_provider()
state, _resp = self._get_state(request, provider)
if state is None:
state = {}
try:
import json
data = json.loads(request.body)
request.GET = data
except json.JSONDecodeError:
return ErrorResponse(
request,
exception=AuthError.INVALID_JSON,
)
if "error" in request.GET or "code" not in request.GET:
# Distinguish cancel from error
auth_error = request.GET.get("error", None)
if auth_error == self.adapter.login_cancelled_error:
error = AuthError.CANCELLED
else:
error = AuthError.UNKNOWN
raise PermissionDenied("405")
app = provider.app
client = self.adapter.get_client(self.request, app)
try:
access_token = self.adapter.get_access_token_data(
request, app, client, pkce_code_verifier=state.get("pkce_code_verifier")
)
token = self.adapter.parse_token(access_token)
if app.pk:
token.app = app
login = self.adapter.complete_login(
request, app, token, response=access_token
)
login.token = token
login.state = state
try:
flows.login.complete_login(request, login)
except ValidationError as e:
return ErrorResponse(self.request, exception=e)
except SignupClosedException:
return ForbiddenResponse(self.request)
return AuthenticationResponse(self.request)
except (
PermissionDenied,
OAuth2Error,
RequestException,
ProviderException,
) as e:
return ErrorResponse(
request,
exception=e,
)
oauth2_callback = OAuth2CallbackView.adapter_view(GoogleOAuth2Adapter)
oauth2_callback = app_view(oauth2_callback) The flow is, sequenceDiagram
autonumber
participant NuxtApp as Nuxt App
participant DjangoBackend as Django Backend
participant Google as Google OAuth
NuxtApp->>DjangoBackend: Synchronous call to providerRedirect (client='app')
DjangoBackend->>Google: Redirect to Google OAuth Screen
Google->>NuxtApp: Redirect back with query params
NuxtApp->>DjangoBackend: Call to providerCallback (POST with query params)
DjangoBackend->>DjangoBackend: Convert POST to GET and complete login flow
DjangoBackend->>NuxtApp: AuthenticationResponse (Access Token + Session Token)
I wonder why this flow is also not provided by this library? You might say, provider like Apple uses For provider like 'Facebook', we can update the callback to ask for email in the response, like it's done for MFA. And after that complete the flow and return the tokens; If you want I can help you with a PR. Thank youo for your time. |
Beta Was this translation helpful? Give feedback.
-
Hi everyone,
I’m implementing social login using Google with
django-allauth
in a headless setup. Instead of relying onsessionid
, I want to use JWT for authentication (viaSimpleJWT
or a similar strategy).Here’s my current setup:
django-allauth
.Key Questions:
django-allauth
to fully support JWT tokens instead of sessions for social login?HEADLESS_TOKEN_STRATEGY
?Current Django Settings
I can’t figure out how to return a token upon successful login instead of creating a session.
Any guidance or working example on how to achieve JWT-based social login would be highly appreciated!
Thanks in advance!
Beta Was this translation helpful? Give feedback.
All reactions