Group
Extension

Docker-Registry/lib/Docker/Registry/Auth/AzureServicePrincipal.pm

package Docker::Registry::Auth::AzureServicePrincipal;
  use Moo;
  with 'Docker::Registry::Auth';
  use JSON::MaybeXS;

  use Azure::AD::ClientCredentials;
  has ad_auth => (
    is => 'ro',
    default => sub {
      Azure::AD::ClientCredentials->new(
        resource_id => "https://management.core.windows.net/",       
      );
    }
  );

  has ua => (is => 'ro', default => sub { HTTP::Tiny->new(timeout => 20) });

  sub authorize {
    my ($self, $request, $scope) = @_;

    my ($login_server) = ($request->url =~ m|//(.*?)/v2|);
    die "Can't detect login_server from " . $request->url if (not defined $login_server);

    my $challenge = $self->ua->get("https://$login_server/v2/");
    die "Registry didn't issue a challenge" if ($challenge->{ status } != 401);
    die "Registry didn't issue a challenge" if (not defined $challenge->{ headers }->{ 'www-authenticate' });

    my $authenticate = $challenge->{ headers }->{ 'www-authenticate' };
    my @tokens = split / /, $authenticate, 2;
    die "Registry doesn't support AAD login" if (@tokens < 2 or lc($tokens[0]) ne 'bearer');
    
    my %params = map { my @kv = split /=/, $_; $kv[1] =~ s/"//g; ($kv[0] => $kv[1])  } split /,/, $tokens[1];
    if (not defined $params{ realm } or not defined $params{ service }) {
      die "Registry doesn't support AAD login"
    }

    my $authhost = $params{ realm };
    $authhost =~ s|/oauth2/token|/oauth2/exchange|;

    my $response = $self->ua->post_form(
      $authhost,
      {
        grant_type => 'access_token',
        service => $params{ service },
        tenant => $self->ad_auth->tenant_id,
        access_token => $self->ad_auth->access_token
      }
    );

    die "Access to registry was denied. Response code: $response->{ status }" if (not $response->{ success });

    my $refresh_token = decode_json($response->{ content })->{ refresh_token };

    $response = $self->ua->post_form(
      $params{ realm },
      {
        grant_type => 'refresh_token',
        service => $params{ service },
        scope => $scope,
        refresh_token => $refresh_token,
      }
    );

    my $access_token = decode_json($response->{ content })->{ access_token };

    $request->header('Authorization', 'Bearer ' . $access_token);

    return $request;
  }

1;


Powered by Groonga
Maintained by Kenichi Ishigaki <ishigaki@cpan.org>. If you find anything, submit it on GitHub.