Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion lib/MetaCPAN/Server/Controller/Login.pm
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,18 @@ sub auto : Private {
# Store params in a temporary cookie so we can keep track of them.
# This should include `client_id` (metacpan env) and `choice` (provider).
if ( $c->req->params->{client_id} ) {

# The OAuth handshake can start on one *.metacpan.org host and finish
# on another (e.g. the provider callback host), so a host-only cookie
# would be lost in transit. `oauth_cookie_domain` (config) scopes it to
# the shared parent domain in production; an empty value keeps it
# host-only for local dev.
my $domain = $c->config->{oauth_cookie_domain};
$c->res->cookies->{oauth_tmp} = {
value => encode_json( $c->req->parameters ),
path => '/',
expires => '+7d'
expires => '+7d',
( $domain ? ( domain => $domain ) : () ),
};
}

Expand Down
7 changes: 7 additions & 0 deletions metacpan_server.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,10 @@ oauth:
secret: seekrit

front_end_url: http://0.0.0.0:5001

# Domain for the temporary OAuth cookie (oauth_tmp), scoped to the shared
# parent domain so the login handshake can start and finish on different hosts
# (e.g. api.metacpan.org and the provider callback host). For local development
# override this in metacpan_server_local.yaml (an empty value keeps the cookie
# host-only).
oauth_cookie_domain: .metacpan.org
3 changes: 3 additions & 0 deletions metacpan_server_testing.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,6 @@ oauth:
secret: seekrit

front_end_url: http://0.0.0.0:5001

# See metacpan_server.yaml.
oauth_cookie_domain: .metacpan.org
50 changes: 50 additions & 0 deletions t/server/controller/login/oauth_cookie.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use strict;
use warnings;
use lib 't/lib';

use MetaCPAN::Server::Test qw( app GET test_psgi );
use Test::More;

# The `oauth_tmp` cookie carries the OAuth params from the leg that starts the
# flow to the leg that finishes it (the provider callback). When those legs land
# on different *.metacpan.org hosts a host-only cookie is lost and login
# silently fails, so the cookie domain is configurable via `oauth_cookie_domain`
# (set to the shared parent domain in production). We ship a sane default and
# allow it to be overridden -- or disabled (host-only) with an empty value.

test_psgi app, sub {
my $cb = shift;

subtest 'applies the cookie domain from config' => sub {
my $domain = MetaCPAN::Server->config->{oauth_cookie_domain};
ok defined $domain && length $domain,
'a default oauth_cookie_domain is configured';

my $res = $cb->( GET '/login/github?client_id=metacpan.dev' );
like $res->header('Set-Cookie'), qr/oauth_tmp=/,
'oauth_tmp cookie set';
like $res->header('Set-Cookie'), qr/domain=\Q$domain\E/i,
"Set-Cookie carries the configured domain ($domain)";
};

subtest 'config override is honored' => sub {
my $config = MetaCPAN::Server->config;
local $config->{oauth_cookie_domain} = '.example.test';

my $res = $cb->( GET '/login/github?client_id=metacpan.dev' );
like $res->header('Set-Cookie'), qr/domain=\.example\.test/i,
'overridden domain applied';
};

subtest 'empty oauth_cookie_domain disables the domain (host-only)' =>
sub {
my $config = MetaCPAN::Server->config;
local $config->{oauth_cookie_domain} = q{};

my $res = $cb->( GET '/login/github?client_id=metacpan.dev' );
unlike $res->header('Set-Cookie'), qr/domain=/i,
'no Domain attribute when explicitly disabled';
};
};

done_testing;