Skip to content
This repository has been archived by the owner on Nov 7, 2022. It is now read-only.

Commit

Permalink
support OpenID Connect.
Browse files Browse the repository at this point in the history
  • Loading branch information
s-aska committed May 9, 2015
1 parent 2010fb7 commit d8c13a2
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 126 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -6,6 +6,7 @@ MANIFEST
*.key
*.secret
*.crt
*.id
.carton
MYMETA.*
pm_to_blib
Expand Down
2 changes: 2 additions & 0 deletions lib/DoubleSpark/Config.pm
Expand Up @@ -44,6 +44,8 @@ sub new {
callback_url => "$args->{base_url}/signin/dropbox/callback"
},
OpenID => {
client_id_file => 'config/google.id',
client_secret_file => 'config/google.secret',
return_to => "$args->{base_url}/signin/google/callback",
realm => "$args->{base_url}/"
},
Expand Down
261 changes: 135 additions & 126 deletions lib/DoubleSpark/Web/C/Signin/Google.pm
Expand Up @@ -5,6 +5,16 @@ use DoubleSpark::API::Account;
use Net::OpenID::Consumer::Lite;
use Log::Minimal;
use Digest::MD5 qw(md5_hex);
use String::Random qw(random_regex);
use Furl;
use Path::Class;
use JSON::XS;

my $furl = Furl->new(
agent => '7kai Tasks',
timeout => 10,
);
my $json = JSON::XS->new->ascii;

sub signin {
my ($self, $c) = @_;
Expand All @@ -17,32 +27,18 @@ sub signin {
critf('signin.twitter.callback %s', $error);
$c->redirect('/', { google_error => 1 });
}

my $state = random_regex('[a-zA-Z0-9]{24}');
my %query = (
'openid.ns' => 'http://specs.openid.net/auth/2.0',
'openid.claimed_id' => 'http://specs.openid.net/auth/2.0/identifier_select',
'openid.identity' => 'http://specs.openid.net/auth/2.0/identifier_select',
'openid.return_to' => $config->{OpenID}->{return_to},
'openid.realm' => $config->{OpenID}->{realm},
'openid.mode' => 'checkid_setup'
client_id => file($c->base_dir(), $config->{OpenID}->{client_id_file})->slurp,
scope => 'openid profile email ',
response_type => 'code',
state => $state,
redirect_uri => $config->{OpenID}->{return_to},
openid_realm => $config->{OpenID}->{realm},
);
my @required = ('email');
# my @required = $c->req->param('openid.ax.required');
if (@required) {
my $axschema = {
country => 'http://axschema.org/contact/country/home',
email => 'http://axschema.org/contact/email',
firstname => 'http://axschema.org/namePerson/first',
language => 'http://axschema.org/pref/language',
lastname => 'http://axschema.org/namePerson/last'
};
for my $type (@required) {
$query{'openid.ax.type.'.$type} = $axschema->{$type};
}
$query{'openid.ax.required'} = join(',', @required);
$query{'openid.ns.ax'} = 'http://openid.net/srv/ax/1.0';
$query{'openid.ax.mode'} = 'fetch_request';
}
my $google_url = URI->new('https://www.google.com/accounts/o8/ud');
$c->session->set('state', $state);
my $google_url = URI->new('https://accounts.google.com/o/oauth2/auth');
$google_url->query_form(%query);
my $check_url = $google_url->as_string;
$c->redirect($check_url);
Expand All @@ -51,110 +47,123 @@ sub signin {
sub callback {
my ($self, $c) = @_;

my $request = $c->req->parameters->mixed;
my $account = $c->account;
my $config = $c->config;
my $account = $c->account;
my $next_url = $c->session->remove('next_url') || '/';

Net::OpenID::Consumer::Lite->handle_server_response(
$request => (
not_openid => sub {
die "Not an OpenID message";
},
setup_required => sub {
my $setup_url = shift;
$c->redirect($setup_url);
},
cancelled => sub {
$c->redirect('/', { google_cancelled => 1 });
},
verified => sub {
my $vident = shift;
my $code = $c->req->param('openid.ext1.value.email');
my ($screen_name, $domain) = split '@', $code;
my $icon = 'https://secure.gravatar.com/avatar/' . md5_hex($code);
my $google_account = $c->db->single('google_account', {
code => $code
});

# 既存のアカウント
if ($google_account) {

# 引越し
if ($account && $account->account_id != $google_account->account_id) {
$google_account->update({
account_id => $account->account_id,
authenticated_on => \'now()'
});
infof('move google_account %s aid:%s to aid:%s',
$code,
$google_account->account_id,
$account->account_id);
$c->session->set('sign', {
account_id => $account->account_id,
code => $code,
name => $account->data->{name},
icon => $account->data->{icon}
});
$c->session->set('notice', 'google_account_move');
}

# 通常サインイン
else {
$google_account->update({
authenticated_on => \'now()'
});
infof('signin from google %s', $code);
$account ||= $c->db->single('account', { account_id => $google_account->account_id });
$c->session->set('sign', {
account_id => $google_account->account_id,
code => $code,
name => $account->data->{name},
icon => $account->data->{icon}
});
}
} else {

# 追加
if ($account) {
infof('add google_account aid:%s code:%s', $account->account_id, $code);
$c->session->set('notice', 'google_account_add');
}

# 新規作成
else {
$account = DoubleSpark::API::Account->create($c, $code, $screen_name, $icon);

infof('new google_account aid:%s code:%s', $account->account_id, $code);
$c->session->set('notice', 'google_account_create');
}

my $google_account = $c->db->insert('google_account', {
account_id => $account->account_id,
code => $code,
name => $screen_name,
data => {},
authenticated_on => \'now()',
created_on => \'now()',
updated_on => \'now()'
});

$c->session->set('sign', {
account_id => $account->account_id,
code => $code,
name => $account->data->{name},
icon => $account->data->{icon}
});
}
$c->session->regenerate_session_id(1);
$c->redirect($next_url);
},
error => sub {
my $error = shift;
critf('signin.google.callback %s', $error);
$c->redirect('/', { google_error => 1 });
}
)
);
my $request_state = $c->req->param('state') // '';
my $session_state = $c->session->get('state') // '';
if ($session_state ne $request_state) {
$c->redirect('/', { google_error => 'state is invalid.' });
}

if (my $error = $c->req->param('error')) {
critf('signin.google.callback %s', $error);
$c->redirect('/', { google_error => $error });
}

my $res = $furl->post('https://accounts.google.com/o/oauth2/token', [], {
code => $c->req->param('code') // '',
client_id => file($c->base_dir(), $config->{OpenID}->{client_id_file})->slurp,
client_secret => file($c->base_dir(), $config->{OpenID}->{client_secret_file})->slurp,
redirect_uri => $config->{OpenID}->{return_to},
grant_type => 'authorization_code',
});

unless ($res->is_success) {
$c->redirect('/', { google_error => 2 });
}

my $data = $json->decode($res->decoded_content);
my $profile_res = $furl->get('https://www.googleapis.com/plus/v1/people/me/openIdConnect', [
'Authorization', 'OAuth ' . $data->{access_token}
]);

unless ($profile_res->is_success) {
$c->redirect('/', { google_error => 3 });
}

my $profile = $json->decode($profile_res->decoded_content);

my $code = $profile->{email};
my $icon = $profile->{picture};
my $screen_name = $profile->{name};

my $google_account = $c->db->single('google_account', {
code => $code
});

# 既存のアカウント
if ($google_account) {

# 引越し
if ($account && $account->account_id != $google_account->account_id) {
$google_account->update({
account_id => $account->account_id,
authenticated_on => \'now()'
});
infof('move google_account %s aid:%s to aid:%s',
$code,
$google_account->account_id,
$account->account_id);
$c->session->set('sign', {
account_id => $account->account_id,
code => $code,
name => $account->data->{name},
icon => $account->data->{icon}
});
$c->session->set('notice', 'google_account_move');
}

# 通常サインイン
else {
$google_account->update({
authenticated_on => \'now()'
});
infof('signin from google %s', $code);
$account ||= $c->db->single('account', { account_id => $google_account->account_id });
$c->session->set('sign', {
account_id => $google_account->account_id,
code => $code,
name => $account->data->{name},
icon => $account->data->{icon}
});
}
} else {

# 追加
if ($account) {
infof('add google_account aid:%s code:%s', $account->account_id, $code);
$c->session->set('notice', 'google_account_add');
}

# 新規作成
else {
$account = DoubleSpark::API::Account->create($c, $code, $screen_name, $icon);

infof('new google_account aid:%s code:%s', $account->account_id, $code);
$c->session->set('notice', 'google_account_create');
}

my $google_account = $c->db->insert('google_account', {
account_id => $account->account_id,
code => $code,
name => $screen_name,
data => {},
authenticated_on => \'now()',
created_on => \'now()',
updated_on => \'now()'
});

$c->session->set('sign', {
account_id => $account->account_id,
code => $code,
name => $account->data->{name},
icon => $account->data->{icon}
});
}
$c->session->regenerate_session_id(1);
$c->redirect($next_url);
}

1;

0 comments on commit d8c13a2

Please sign in to comment.