Merge pull request #841 from pingiun/github-login

Implement GitHub logins
This commit is contained in:
Eelco Dolstra 2021-01-05 14:51:51 +01:00 committed by GitHub
commit be0aa7eb85
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 78 additions and 10 deletions

View file

@ -30,6 +30,8 @@ sub noLoginNeeded {
return $whitelisted ||
$c->request->path eq "api/push-github" ||
$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 "logo" ||
$c->request->path =~ /^static\//;

View file

@ -4,6 +4,7 @@ use utf8;
use strict;
use warnings;
use base 'Hydra::Base::Controller::REST';
use File::Slurp;
use Crypt::RandPasswd;
use Digest::SHA1 qw(sha1_hex);
use Hydra::Helper::Nix;
@ -154,6 +155,67 @@ sub google_login :Path('/google-login') Args(0) {
doEmailLogin($self, $c, "google", $data->{email}, $data->{name} // undef);
}
sub github_login :Path('/github-login') Args(0) {
my ($self, $c) = @_;
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/emails', Accept => 'application/vnd.github.v3+json', Authorization => "token $access_token");
error($c, "Did not get a response from GitHub for email info.") unless $response->is_success;
$data = decode_json($response->decoded_content) or die;
my $email;
foreach my $eml (@{$data}) {
$email = $eml->{email} if $eml->{verified} && $eml->{primary};
}
die "No primary email for this GitHub profile" unless $email;
$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", $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) = @_;
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&scope=user:email");
}
sub captcha :Local Args(0) {
my ($self, $c) = @_;

View file

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

View file

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

View file

@ -10,7 +10,7 @@ create table Users (
emailAddress text not null,
password text not null, -- sha256 hash
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
);