use more minimal payload for QR code sake
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
parent
fc5f5ed117
commit
cb60fc2c7b
|
@ -7,12 +7,19 @@ from django.utils.translation import gettext_lazy as _
|
||||||
from django.views import View
|
from django.views import View
|
||||||
from django_otp.models import Device
|
from django_otp.models import Device
|
||||||
from rest_framework.serializers import BaseSerializer, Serializer
|
from rest_framework.serializers import BaseSerializer, Serializer
|
||||||
|
from authentik.core.models import ExpiringModel
|
||||||
|
|
||||||
from authentik.core.types import UserSettingSerializer
|
from authentik.core.types import UserSettingSerializer
|
||||||
from authentik.flows.models import ConfigurableStage, FriendlyNamedStage, Stage
|
from authentik.flows.models import ConfigurableStage, FriendlyNamedStage, Stage
|
||||||
|
from authentik.lib.generators import generate_id
|
||||||
from authentik.lib.models import SerializerModel
|
from authentik.lib.models import SerializerModel
|
||||||
|
|
||||||
|
|
||||||
|
def default_token_key():
|
||||||
|
"""Default token key"""
|
||||||
|
return generate_id(40)
|
||||||
|
|
||||||
|
|
||||||
class AuthenticatorMobileStage(ConfigurableStage, FriendlyNamedStage, Stage):
|
class AuthenticatorMobileStage(ConfigurableStage, FriendlyNamedStage, Stage):
|
||||||
"""Setup Duo authenticator devices"""
|
"""Setup Duo authenticator devices"""
|
||||||
|
|
||||||
|
@ -70,3 +77,9 @@ class MobileDevice(SerializerModel, Device):
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("Mobile Device")
|
verbose_name = _("Mobile Device")
|
||||||
verbose_name_plural = _("Mobile Devices")
|
verbose_name_plural = _("Mobile Devices")
|
||||||
|
|
||||||
|
class MobileDeviceToken(ExpiringModel):
|
||||||
|
|
||||||
|
device = models.ForeignKey(MobileDevice, on_delete=models.CASCADE, null=True)
|
||||||
|
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
|
||||||
|
token = models.TextField(default=default_token_key)
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from rest_framework.fields import CharField
|
from rest_framework.fields import CharField
|
||||||
|
from authentik.core.api.utils import PassiveSerializer
|
||||||
|
|
||||||
from authentik.events.models import Event, EventAction
|
from authentik.events.models import Event, EventAction
|
||||||
from authentik.flows.challenge import (
|
from authentik.flows.challenge import (
|
||||||
|
@ -11,16 +12,22 @@ from authentik.flows.challenge import (
|
||||||
WithUserInfoChallenge,
|
WithUserInfoChallenge,
|
||||||
)
|
)
|
||||||
from authentik.flows.stage import ChallengeStageView
|
from authentik.flows.stage import ChallengeStageView
|
||||||
from authentik.stages.authenticator_mobile.models import AuthenticatorMobileStage
|
from authentik.stages.authenticator_mobile.models import AuthenticatorMobileStage, MobileDeviceToken
|
||||||
|
|
||||||
SESSION_KEY_MOBILE_ENROLL = "authentik/stages/authenticator_mobile/enroll"
|
FLOW_PLAN_MOBILE_ENROLL = "authentik/stages/authenticator_mobile/enroll"
|
||||||
|
|
||||||
|
|
||||||
|
class AuthenticatorMobilePayloadChallenge(PassiveSerializer):
|
||||||
|
"""Payload within the QR code given to the mobile app, hence the short variable names"""
|
||||||
|
|
||||||
|
u = CharField(required=False, help_text="Server URL")
|
||||||
|
s = CharField(required=False, help_text="Stage UUID")
|
||||||
|
t = CharField(required=False, help_text="Initial Token")
|
||||||
|
|
||||||
class AuthenticatorMobileChallenge(WithUserInfoChallenge):
|
class AuthenticatorMobileChallenge(WithUserInfoChallenge):
|
||||||
"""Mobile Challenge"""
|
"""Mobile Challenge"""
|
||||||
|
|
||||||
authentik_url = CharField(required=True)
|
payload = AuthenticatorMobilePayloadChallenge(required=True)
|
||||||
stage_uuid = CharField(required=True)
|
|
||||||
component = CharField(default="ak-stage-authenticator-mobile")
|
component = CharField(default="ak-stage-authenticator-mobile")
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,13 +42,28 @@ class AuthenticatorMobileStageView(ChallengeStageView):
|
||||||
|
|
||||||
response_class = AuthenticatorMobileChallengeResponse
|
response_class = AuthenticatorMobileChallengeResponse
|
||||||
|
|
||||||
|
def prepare(self):
|
||||||
|
if FLOW_PLAN_MOBILE_ENROLL in self.executor.plan.context:
|
||||||
|
return
|
||||||
|
token = MobileDeviceToken.objects.create(
|
||||||
|
user=self.get_pending_user(),
|
||||||
|
)
|
||||||
|
self.executor.plan.context[FLOW_PLAN_MOBILE_ENROLL] = token
|
||||||
|
|
||||||
def get_challenge(self, *args, **kwargs) -> Challenge:
|
def get_challenge(self, *args, **kwargs) -> Challenge:
|
||||||
stage: AuthenticatorMobileStage = self.executor.current_stage
|
stage: AuthenticatorMobileStage = self.executor.current_stage
|
||||||
|
self.prepare()
|
||||||
|
payload = AuthenticatorMobilePayloadChallenge(data={
|
||||||
|
# TODO: use cloud gateway?
|
||||||
|
"u": self.request.get_host(),
|
||||||
|
"s": str(stage.stage_uuid),
|
||||||
|
"t": self.executor.plan[FLOW_PLAN_MOBILE_ENROLL].token,
|
||||||
|
})
|
||||||
|
payload.is_valid()
|
||||||
return AuthenticatorMobileChallenge(
|
return AuthenticatorMobileChallenge(
|
||||||
data={
|
data={
|
||||||
"type": ChallengeTypes.NATIVE.value,
|
"type": ChallengeTypes.NATIVE.value,
|
||||||
"authentik_url": self.request.get_host(),
|
"payload": payload.validated_data,
|
||||||
"stage_uuid": str(stage.stage_uuid),
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
23
schema.yml
23
schema.yml
|
@ -30183,15 +30183,12 @@ components:
|
||||||
type: string
|
type: string
|
||||||
pending_user_avatar:
|
pending_user_avatar:
|
||||||
type: string
|
type: string
|
||||||
authentik_url:
|
payload:
|
||||||
type: string
|
$ref: '#/components/schemas/AuthenticatorMobilePayloadChallenge'
|
||||||
stage_uuid:
|
|
||||||
type: string
|
|
||||||
required:
|
required:
|
||||||
- authentik_url
|
- payload
|
||||||
- pending_user
|
- pending_user
|
||||||
- pending_user_avatar
|
- pending_user_avatar
|
||||||
- stage_uuid
|
|
||||||
- type
|
- type
|
||||||
AuthenticatorMobileChallengeResponseRequest:
|
AuthenticatorMobileChallengeResponseRequest:
|
||||||
type: object
|
type: object
|
||||||
|
@ -30201,6 +30198,20 @@ components:
|
||||||
type: string
|
type: string
|
||||||
minLength: 1
|
minLength: 1
|
||||||
default: ak-stage-authenticator-mobile
|
default: ak-stage-authenticator-mobile
|
||||||
|
AuthenticatorMobilePayloadChallenge:
|
||||||
|
type: object
|
||||||
|
description: Payload within the QR code given to the mobile app, hence the short
|
||||||
|
variable names
|
||||||
|
properties:
|
||||||
|
u:
|
||||||
|
type: string
|
||||||
|
description: Server URL
|
||||||
|
s:
|
||||||
|
type: string
|
||||||
|
description: Stage UUID
|
||||||
|
t:
|
||||||
|
type: string
|
||||||
|
description: Initial Token
|
||||||
AuthenticatorMobileStage:
|
AuthenticatorMobileStage:
|
||||||
type: object
|
type: object
|
||||||
description: AuthenticatorMobileStage Serializer
|
description: AuthenticatorMobileStage Serializer
|
||||||
|
|
|
@ -338,7 +338,9 @@ export class FlowExecutor extends Interface implements StageHost {
|
||||||
.challenge=${this.challenge}
|
.challenge=${this.challenge}
|
||||||
></ak-stage-authenticator-duo>`;
|
></ak-stage-authenticator-duo>`;
|
||||||
case "ak-stage-authenticator-mobile":
|
case "ak-stage-authenticator-mobile":
|
||||||
await import("@goauthentik/flow/stages/authenticator_mobile/AuthenticatorMobileStage");
|
await import(
|
||||||
|
"@goauthentik/flow/stages/authenticator_mobile/AuthenticatorMobileStage"
|
||||||
|
);
|
||||||
return html`<ak-stage-authenticator-mobile
|
return html`<ak-stage-authenticator-mobile
|
||||||
.host=${this as StageHost}
|
.host=${this as StageHost}
|
||||||
.challenge=${this.challenge}
|
.challenge=${this.challenge}
|
||||||
|
|
|
@ -70,7 +70,7 @@ export class AuthenticatorMobileStage extends BaseStage<
|
||||||
</div>
|
</div>
|
||||||
</ak-form-static>
|
</ak-form-static>
|
||||||
<div class="qr-container">
|
<div class="qr-container">
|
||||||
<qr-code data="${JSON.stringify(this.challenge)}"></qr-code>
|
<qr-code data="${JSON.stringify(this.challenge.payload)}"></qr-code>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
Reference in New Issue