Mojo-ATProto-OAuth

 view release on metacpan or  search on metacpan

lib/Mojo/ATProto/OAuth/ClientMetadata.pm  view on Meta::CPAN

package 
    Mojo::ATProto::OAuth::ClientMetadata;

use Mojo::Base -base, -signatures;
use Mojo::ATProto::OAuth::DPoP qw//;
use Mojo::JSON qw/decode_json true/;
use Mojo::Log  qw//;

use constant DEBUG => $ENV{MOJO_OAUTH_DEBUG} || 0;
my $LOG = Mojo::Log->new;

# Builds the "client ID metadata document" - the JSON document ATProto
# OAuth's client-ID-metadata-document flow requires be served, byte for
# byte, at the client_id URL itself (no separate client registration
# step). 
#
# %config keys: client_id, callback_url, scopes (arrayref, must include
# 'atproto'), private_key + key_id (optional Crypt::PK::ECC keypair -
# presence makes this a confidential client; omit both for a public
# client).
sub build ($class, %config) {
    my $client_id    = $config{client_id}    // die "build: 'client_id' required\n";
    my $callback_url = $config{callback_url} // die "build: 'callback_url' required\n";
    my $scopes       = $config{scopes}       // die "build: 'scopes' required\n";

    my $doc = {
        client_id                   => $client_id,
        application_type            => 'web',
        grant_types                 => ['authorization_code', 'refresh_token'],
        scope                       => join(' ', @$scopes),
        response_types              => ['code'],
        redirect_uris               => [$callback_url],
        dpop_bound_access_tokens    => true,
        token_endpoint_auth_method  => 'none',
    };

    if (defined($config{private_key}) && defined($config{key_id})) {
        $doc->{token_endpoint_auth_method}    = 'private_key_jwt';
        $doc->{token_endpoint_auth_signing_alg} = 'ES256';
        $doc->{jwks}                           = $class->public_jwks(%config);
    }

    $LOG->debug("ClientMetadata: built document for client_id=$client_id (confidential="
        . (defined($config{private_key}) ? 'yes' : 'no') . ')') if DEBUG;

    return $doc;
}

# Returns a JWKS document ({keys => [...]}) exposing the client's public
# assertion key - only meaningful for confidential clients. Callable on
# its own (not just via build()), so a public client's missing key
# still gets a safe {keys => []} rather than dying - but build() itself
# only sets this on the *document* for a confidential client; a public
# client's document omits the `jwks` key entirely, it doesn't get an
# empty one.
sub public_jwks ($class, %config) {
    return {keys => []} unless defined($config{private_key}) && defined($config{key_id});

    my $pub = $config{private_key}->export_key_jwk('public');
    my $jwk = decode_json($pub);
    $jwk->{kid} = $config{key_id};

    return {keys => [$jwk]};
}

1;



( run in 1.482 second using v1.01-cache-2.11-cpan-8dfa8b56332 )