# Self U2F registration
package Lemonldap::NG::Portal::Register::U2F;

use strict;
use Mouse;

our $VERSION = '2.0.0';

extends 'Lemonldap::NG::Portal::Lib::U2F';

# INITIALIZATION

sub init {
    my ($self) = @_;
    return 0 unless $self->SUPER::init;
    $self->addAuthRoute( u2fregister => { ':action' => 'run' }, ['POST'] );
    $self->addAuthRoute( 'u2fregister.html' => undef, ['GET'] );
    return 1;
}

# RUNNING METHODS

# Main method
sub run {
    my ( $self, $req ) = @_;
    my $action = $req->param('action');

    if ( $action eq 'register' ) {
        my $challenge = $self->crypter->registrationChallenge;
        return [ 200, [ 'Content-Type' => 'application/json' ], [$challenge] ];
    }
    if ( $action eq 'registration' ) {
        my $resp;
        unless ( $resp = $req->param('registration') ) {
            return $self->p->sendError( $req, 'Missing registration parameter',
                400 );
        }
        $self->logger->debug("Get registration data $resp");
        my ( $keyHandle, $userKey ) = $self->crypter->registrationVerify($resp);
        if ( $keyHandle and $userKey ) {
            $self->p->updatePersistentSession(
                $req,
                {
                    _u2fKeyHandle => $self->encode_base64url( $keyHandle, '' ),
                    _u2fUserKey   => $self->encode_base64url( $userKey,   '' )
                }
            );
            return [
                200, [ 'Content-Type' => 'application/json' ],
                ['{"result":1}']
            ];
        }
        my $err = Crypt::U2F::Server::Simple::lastError();
        $self->userLogger->warn("U2F Registration failed: $err");
        return $self->p->sendError( $req, $err, 200 );
    }
    if ( $action eq 'verify' ) {
        my ( $err, $error ) = $self->loadUser($req);
        if ( $err == -1 ) {
            return $self->p->sendError( $req, "U2F error: $error", 200 );
        }
        elsif ( $err == 0 ) {
            return $self->p->sendError( $req, "noU2FKeyFound" );
        }
        my $challenge = $self->crypter->authenticationChallenge;
        return [ 200, [ 'Content-Type' => 'application/json' ], [$challenge] ];
    }
    if ( $action eq 'signature' ) {
        my $resp;
        unless ( $resp = $req->param('signature') ) {
            return $self->p->sendError( $req, 'Missing signature parameter',
                400 );
        }
        my ( $err, $error ) = $self->loadUser($req);
        if ( $err == -1 ) {
            return $self->p->sendError( $req, "U2F error: $error", 200 );
        }
        elsif ( $err == 0 ) {
            return $self->p->sendError( $req, "noU2FKeyFound" );
        }
        my $res = ( $self->crypter->authenticationVerify($resp) ? 1 : 0 );
        return [
            200, [ 'Content-Type' => 'application/json' ],
            [qq'{"result":$res}']
        ];
    }
}

sub loadUser {
    my ( $self, $req ) = @_;
    my $uid     = $req->userData->{ $self->conf->{whatToTrace} };
    my $session = $self->p->getPersistentSession($uid);
    my $kh      = $session->data->{_u2fKeyHandle};
    my $uk      = $session->data->{_u2fUserKey};
    unless ( $kh and $uk ) {
        return 0;
    }
    $self->crypter->{keyHandle} = $self->decode_base64url($kh);
    $self->crypter->{publicKey} = $self->decode_base64url($uk);
    unless ( $self->crypter->setKeyHandle and $self->crypter->setPublicKey ) {
        my $error = Crypt::U2F::Server::Simple::lastError();
        return ( -1, $error );
    }
    return 1;
}

1;
