Implement GitHub logins

Requires the following configuration options
enable_github_login = 1
github_client_id
github_client_secret
Or github_client_secret_file which points to a file with the secret
This commit is contained in:
Jelle Besseling 2020-12-26 17:58:16 +01:00
parent bde8d81876
commit bbd4891133
No known key found for this signature in database
GPG key ID: 9712452E8BE3372E
5 changed files with 68 additions and 10 deletions

View file

@ -30,6 +30,8 @@ sub noLoginNeeded {
return $whitelisted || return $whitelisted ||
$c->request->path eq "api/push-github" || $c->request->path eq "api/push-github" ||
$c->request->path eq "google-login" || $c->request->path eq "google-login" ||
$c->request->path eq "github-redirect" ||
$c->request->path eq "github-login" ||
$c->request->path eq "login" || $c->request->path eq "login" ||
$c->request->path eq "logo" || $c->request->path eq "logo" ||
$c->request->path =~ /^static\//; $c->request->path =~ /^static\//;

View file

@ -4,6 +4,7 @@ use utf8;
use strict; use strict;
use warnings; use warnings;
use base 'Hydra::Base::Controller::REST'; use base 'Hydra::Base::Controller::REST';
use File::Slurp;
use Crypt::RandPasswd; use Crypt::RandPasswd;
use Digest::SHA1 qw(sha1_hex); use Digest::SHA1 qw(sha1_hex);
use Hydra::Helper::Nix; use Hydra::Helper::Nix;
@ -154,6 +155,57 @@ sub google_login :Path('/google-login') Args(0) {
doEmailLogin($self, $c, "google", $data->{email}, $data->{name} // undef); doEmailLogin($self, $c, "google", $data->{email}, $data->{name} // undef);
} }
sub github_login :Path('/github-login') Args(0) {
my ($self, $c) = @_;
error($c, "Logging in via GitHub is not enabled.") unless $c->config->{enable_github_login};
my $client_id = $c->config->{github_client_id} or die "github_client_id not configured.";
my $client_secret = $c->config->{github_client_secret} // do {
my $client_secret_file = $c->config->{github_client_secret_file} or die "github_client_secret nor github_client_secret_file is configured.";
my $client_secret = read_file($client_secret_file);
$client_secret =~ s/\s+//;
$client_secret;
};
die "No github secret configured" unless $client_secret;
my $ua = new LWP::UserAgent;
my $response = $ua->post(
'https://github.com/login/oauth/access_token',
{
client_id => $client_id,
client_secret => $client_secret,
code => ($c->req->params->{code} // die "No token."),
}, Accept => 'application/json');
error($c, "Did not get a response from GitHub.") unless $response->is_success;
my $data = decode_json($response->decoded_content) or die;
my $access_token = $data->{access_token} // die "No access_token in response from GitHub.";
$response = $ua->get('https://api.github.com/user', Authorization => "token $access_token");
error($c, "Did not get a response from GitHub for user info.") unless $response->is_success;
$data = decode_json($response->decoded_content) or die;
doEmailLogin($self, $c, "github", $data->{email}, $data->{name} // undef);
$c->res->redirect($c->uri_for($c->res->cookies->{'after_github'}));
}
sub github_redirect :Path('/github-redirect') Args(0) {
my ($self, $c) = @_;
error($c, "Logging in via GitHub is not enabled.") unless $c->config->{enable_github_login};
my $client_id = $c->config->{github_client_id} or die "github_client_id not configured.";
my $after = "/" . $c->req->params->{after};
$c->res->cookies->{'after_github'} = {
name => 'after_github',
value => $after,
};
$c->res->redirect("https://github.com/login/oauth/authorize?client_id=$client_id");
}
sub captcha :Local Args(0) { sub captcha :Local Args(0) {
my ($self, $c) = @_; my ($self, $c) = @_;

View file

@ -136,6 +136,10 @@
<li><a href="#" id="google-signin">Sign in with Google</a></li> <li><a href="#" id="google-signin">Sign in with Google</a></li>
<li class="divider"></li> <li class="divider"></li>
[% END %] [% END %]
[% IF c.config.enable_github_login %]
<li><a href="/github-redirect?after=[% c.req.path %]">Sign in with GitHub</a></li>
<li class="divider"></li>
[% END %]
<li> <li>
<a href="#hydra-signin" data-toggle="modal">Sign in with a Hydra account</a> <a href="#hydra-signin" data-toggle="modal">Sign in with a Hydra account</a>
</li> </li>

View file

@ -11,7 +11,7 @@ sub showHelp {
print <<EOF; print <<EOF;
Usage: $0 NAME Usage: $0 NAME
[--rename-from NAME] [--rename-from NAME]
[--type hydra|google] [--type hydra|google|github]
[--full-name FULLNAME] [--full-name FULLNAME]
[--email-address EMAIL-ADDRESS] [--email-address EMAIL-ADDRESS]
[--password PASSWORD] [--password PASSWORD]
@ -49,8 +49,8 @@ GetOptions("rename-from=s" => \$renameFrom,
die "$0: one user name required\n" if scalar @ARGV != 1; die "$0: one user name required\n" if scalar @ARGV != 1;
my $userName = $ARGV[0]; my $userName = $ARGV[0];
die "$0: type must be `hydra' or `google'\n" die "$0: type must be `hydra', `google' or `github'\n"
if defined $type && $type ne "hydra" && $type ne "google"; if defined $type && $type ne "hydra" && $type ne "google" && $type ne "github";
my $db = Hydra::Model::DB->new(); my $db = Hydra::Model::DB->new();
@ -67,19 +67,19 @@ $db->txn_do(sub {
{ username => $userName, type => "hydra", emailaddress => "", password => "!" }); { username => $userName, type => "hydra", emailaddress => "", password => "!" });
} }
die "$0: Google user names must be email addresses\n" die "$0: Google or GitHub user names must be email addresses\n"
if $user->type eq "google" && $userName !~ /\@/; if ($user->type eq "google" || $user->type eq "github") && $userName !~ /\@/;
$user->update({ type => $type }) if defined $type; $user->update({ type => $type }) if defined $type;
$user->update({ fullname => $fullName eq "" ? undef : $fullName }) if defined $fullName; $user->update({ fullname => $fullName eq "" ? undef : $fullName }) if defined $fullName;
if ($user->type eq "google") { if ($user->type eq "google" || $user->type eq "github") {
die "$0: Google accounts do not have an explicitly set email address.\n" die "$0: Google and GitHub accounts do not have an explicitly set email address.\n"
if defined $emailAddress; if defined $emailAddress;
die "$0: Google accounts do not have a password.\n" die "$0: Google and GitHub accounts do not have a password.\n"
if defined $password; if defined $password;
die "$0: Google accounts do not have a password.\n" die "$0: Google and GitHub accounts do not have a password.\n"
if defined $passwordHash; if defined $passwordHash;
$user->update({ emailaddress => $userName, password => "!" }); $user->update({ emailaddress => $userName, password => "!" });
} else { } else {

View file

@ -10,7 +10,7 @@ create table Users (
emailAddress text not null, emailAddress text not null,
password text not null, -- sha256 hash password text not null, -- sha256 hash
emailOnError integer not null default 0, emailOnError integer not null default 0,
type text not null default 'hydra', -- either "hydra" or "google" type text not null default 'hydra', -- either "hydra", "google" or "github"
publicDashboard boolean not null default false publicDashboard boolean not null default false
); );