aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--perllib/FixMyStreet/App/Controller/Auth/Social.pm28
-rw-r--r--t/Mock/OpenIDConnect.pm1
-rw-r--r--t/app/controller/auth_social.t4
3 files changed, 29 insertions, 4 deletions
diff --git a/perllib/FixMyStreet/App/Controller/Auth/Social.pm b/perllib/FixMyStreet/App/Controller/Auth/Social.pm
index 8ac44c14d..4bd7dcd4d 100644
--- a/perllib/FixMyStreet/App/Controller/Auth/Social.pm
+++ b/perllib/FixMyStreet/App/Controller/Auth/Social.pm
@@ -9,6 +9,8 @@ use Net::Twitter::Lite::WithAPIv1_1;
use OIDC::Lite::Client::WebServer::Azure;
use URI::Escape;
+use mySociety::AuthToken;
+
=head1 NAME
FixMyStreet::App::Controller::Auth::Social - Catalyst Controller
@@ -180,12 +182,14 @@ sub oidc_sign_in : Private {
$c->detach( '/page_error_400_bad_request', [] ) unless $c->cobrand->feature('oidc_login');
my $oidc = $c->forward('oidc');
+ my $nonce = $self->generate_nonce();
my $url = $oidc->uri_to_redirect(
redirect_uri => $c->uri_for('/auth/OIDC'),
scope => 'openid',
- state => 'test',
+ state => 'login',
extra => {
response_mode => 'form_post',
+ nonce => $nonce,
},
);
@@ -193,6 +197,7 @@ sub oidc_sign_in : Private {
$oauth{return_url} = $c->get_param('r');
$oauth{detach_to} = $c->stash->{detach_to};
$oauth{detach_args} = $c->stash->{detach_args};
+ $oauth{nonce} = $nonce;
$c->session->{oauth} = \%oauth;
$c->res->redirect($url);
}
@@ -210,7 +215,7 @@ sub oidc_callback: Path('/auth/OIDC') : Args(0) {
uri => $password_reset_uri,
redirect_uri => $c->uri_for('/auth/OIDC'),
scope => 'openid',
- state => 'test',
+ state => 'password_reset',
extra => {
response_mode => 'form_post',
},
@@ -221,7 +226,14 @@ sub oidc_callback: Path('/auth/OIDC') : Args(0) {
$c->detach('oauth_failure');
}
}
- $c->detach('/page_error_400_bad_request', []) unless $c->get_param('code');
+ $c->detach('/page_error_400_bad_request', []) unless $c->get_param('code') && $c->get_param('state');
+
+ # After a password reset on the OIDC endpoint the user isn't properly logged
+ # in, so redirect them to the usual OIDC login process.
+ $c->detach('oidc_sign_in', []) if $c->get_param('state') eq 'password_reset';
+
+ # The only other valid state param is 'login' at this point.
+ $c->detach('/page_error_400_bad_request', []) unless $c->get_param('state') eq 'login';
my $id_token;
eval {
@@ -239,6 +251,9 @@ sub oidc_callback: Path('/auth/OIDC') : Args(0) {
# sanity check the token audience is us...
$c->detach('/page_error_500_internal_error', ['invalid id_token']) unless $id_token->payload->{aud} eq $c->cobrand->feature('oidc_login')->{client_id};
+ # check that the nonce matches what we set in the user session
+ $c->detach('/page_error_500_internal_error', ['invalid id_token']) unless $id_token->payload->{nonce} eq $c->session->{oauth}{nonce};
+
# Some claims need parsing into a friendlier format
# XXX check how much of this is Westminster/Azure-specific
my $name = join(" ", $id_token->payload->{given_name}, $id_token->payload->{family_name});
@@ -267,6 +282,13 @@ sub oidc_callback: Path('/auth/OIDC') : Args(0) {
$c->forward('oauth_success', [ 'oidc', $uid, $name, $email, $extra ]);
}
+# Just a wrapper around random_token to make mocking easier.
+sub generate_nonce : Private {
+ my ($self, $c) = @_;
+
+ return mySociety::AuthToken::random_token();
+}
+
sub oauth_failure : Private {
my ( $self, $c ) = @_;
diff --git a/t/Mock/OpenIDConnect.pm b/t/Mock/OpenIDConnect.pm
index 3fb15ff70..77ed88817 100644
--- a/t/Mock/OpenIDConnect.pm
+++ b/t/Mock/OpenIDConnect.pm
@@ -48,6 +48,7 @@ sub dispatch_request {
family_name => "Dwyer",
tfp => "B2C_1_default",
extension_CrmContactId => "1c304134-ef12-c128-9212-123908123901",
+ nonce => 'MyAwesomeRandomValue',
};
$payload->{emails} = ['oidc@example.org'] if $self->returns_email;
my $signature = "dummy";
diff --git a/t/app/controller/auth_social.t b/t/app/controller/auth_social.t
index 6c80c9857..3a0539452 100644
--- a/t/app/controller/auth_social.t
+++ b/t/app/controller/auth_social.t
@@ -32,6 +32,8 @@ my $contact2 = $mech->create_contact_ok(
);
my $resolver = Test::MockModule->new('Email::Valid');
+my $social = Test::MockModule->new('FixMyStreet::App::Controller::Auth::Social');
+$social->mock('generate_nonce', sub { 'MyAwesomeRandomValue' });
for my $test (
{
@@ -72,7 +74,7 @@ for my $test (
mock_hosts => ['oidc.example.org'],
host => 'oidc.example.org',
error_callback => '/auth/OIDC?error=ERROR',
- success_callback => '/auth/OIDC?code=response-code',
+ success_callback => '/auth/OIDC?code=response-code&state=login',
redirect_pattern => qr{oidc\.example\.org/oauth2/v2\.0/authorize},
user_extras => [
[westminster_account_id => "1c304134-ef12-c128-9212-123908123901"],