<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use Laravel\Lumen\Routing\Controller as BaseController;
|
|
use Illuminate\Http\Request;
|
|
use GuzzleHttp;
|
|
use DB;
|
|
|
|
class IndieAuth extends BaseController
|
|
{
|
|
private function _redirectURI() {
|
|
return env('BASE_URL') . 'auth/callback';
|
|
}
|
|
|
|
public function start(Request $request) {
|
|
$me = \IndieAuth\Client::normalizeMeURL($request->input('me'));
|
|
if(!$me) {
|
|
return view('auth/error', ['error' => 'Invalid URL']);
|
|
}
|
|
|
|
$state = \IndieAuth\Client::generateStateParameter();
|
|
|
|
if(preg_match('/https?:\/\/github\.com\/[^ \/]+/', $me)) {
|
|
$authorizationURL = 'https://github.com/login/oauth/authorize'
|
|
. '?client_id=' . env('GITHUB_ID')
|
|
. '&state=' . $state;
|
|
|
|
session([
|
|
'auth_state' => $state,
|
|
'attempted_me' => $me,
|
|
]);
|
|
|
|
} else {
|
|
$authorizationEndpoint = \IndieAuth\Client::discoverAuthorizationEndpoint($me);
|
|
$tokenEndpoint = \IndieAuth\Client::discoverTokenEndpoint($me);
|
|
|
|
session([
|
|
'auth_state' => $state,
|
|
'attempted_me' => $me,
|
|
'authorization_endpoint' => $authorizationEndpoint,
|
|
'token_endpoint' => $tokenEndpoint
|
|
]);
|
|
|
|
// If the user specified only an authorization endpoint, use that
|
|
if(!$authorizationEndpoint) {
|
|
// Otherwise, fall back to indieauth.com
|
|
$authorizationEndpoint = env('DEFAULT_AUTH_ENDPOINT');
|
|
}
|
|
$authorizationURL = \IndieAuth\Client::buildAuthorizationURL($authorizationEndpoint, $me, $this->_redirectURI(), env('BASE_URL'), $state);
|
|
}
|
|
|
|
return redirect($authorizationURL);
|
|
}
|
|
|
|
public function callback(Request $request) {
|
|
if(!session('auth_state') || !session('attempted_me')) {
|
|
return view('auth/error', ['error' => 'Missing state information. Start over.']);
|
|
}
|
|
|
|
if($request->input('error')) {
|
|
return view('auth/error', ['error' => $request->input('error')]);
|
|
}
|
|
|
|
if(session('auth_state') != $request->input('state')) {
|
|
return view('auth/error', ['error' => 'State did not match. Start over.']);
|
|
}
|
|
|
|
$tokenEndpoint = false;
|
|
if(session('token_endpoint')) {
|
|
$tokenEndpoint = session('token_endpoint');
|
|
} else if(session('authorization_endpoint')) {
|
|
$authorizationEndpoint = session('authorization_endpoint');
|
|
} else {
|
|
$authorizationEndpoint = env('DEFAULT_AUTH_ENDPOINT');
|
|
}
|
|
if($tokenEndpoint) {
|
|
$token = \IndieAuth\Client::getAccessToken($tokenEndpoint, $request->input('code'), session('attempted_me'), $this->_redirectURI(), env('BASE_URL'), $request->input('state'));
|
|
} else {
|
|
$token = \IndieAuth\Client::verifyIndieAuthCode($authorizationEndpoint, $request->input('code'), session('attempted_me'), $this->_redirectURI(), env('BASE_URL'), $request->input('state'));
|
|
}
|
|
|
|
if($token && array_key_exists('me', $token)) {
|
|
session()->flush();
|
|
session(['me' => $token['me']]);
|
|
$this->_userLoggedIn($token['me']);
|
|
}
|
|
|
|
return redirect('/');
|
|
}
|
|
|
|
public function github(Request $request) {
|
|
if(!session('auth_state') || !session('attempted_me')) {
|
|
return view('auth/error', ['error' => 'Missing state information. Start over.']);
|
|
}
|
|
|
|
if($request->input('error')) {
|
|
return view('auth/error', ['error' => $request->input('error')]);
|
|
}
|
|
|
|
if(session('auth_state') != $request->input('state')) {
|
|
return view('auth/error', ['error' => 'State did not match. Start over.']);
|
|
}
|
|
|
|
if(!$request->input('code')) {
|
|
return view('auth/error', ['error' => 'An unknown error occurred']);
|
|
}
|
|
|
|
$client = new GuzzleHttp\Client([
|
|
'http_errors' => false
|
|
]);
|
|
$res = $client->post('https://github.com/login/oauth/access_token', [
|
|
'form_params' => [
|
|
'client_id' => env('GITHUB_ID'),
|
|
'client_secret' => env('GITHUB_SECRET'),
|
|
// 'redirect_uri' => env('BASE_URL') . 'auth/github',
|
|
'code' => $request->input('code'),
|
|
'state' => session('auth_state')
|
|
],
|
|
'headers' => [
|
|
'Accept' => 'application/json'
|
|
]
|
|
]);
|
|
if($res->getStatusCode() == 200) {
|
|
$body = $res->getBody();
|
|
$data = json_decode($body);
|
|
if($data) {
|
|
if(property_exists($data, 'access_token')) {
|
|
|
|
// Now check the username of the user that just logged in
|
|
$res = $client->get('https://api.github.com/user', [
|
|
'headers' => [
|
|
'Authorization' => 'token ' . $data->access_token
|
|
]
|
|
]);
|
|
if($res->getStatusCode() == 200) {
|
|
$data = json_decode($res->getBody());
|
|
if(property_exists($data, 'login')) {
|
|
session()->flush();
|
|
$me = 'https://github.com/' . $data->login;
|
|
session(['me' => $me]);
|
|
$this->_userLoggedIn($me);
|
|
return redirect('/');
|
|
} else {
|
|
return view('auth/error', ['error' => 'Login failed']);
|
|
}
|
|
} else {
|
|
return view('auth/error', ['error' => 'Login failed']);
|
|
}
|
|
} else {
|
|
$err = '';
|
|
if(property_exists($data, 'error_description')) {
|
|
$err = ': ' . $data->error_description;
|
|
}
|
|
return view('auth/error', ['error' => 'Login failed' . $err]);
|
|
}
|
|
} else {
|
|
return view('auth/error', ['error' => 'Error parsing response body from GitHub']);
|
|
}
|
|
} else {
|
|
return view('auth/error', ['error' => 'Could not verify login from GitHub: ' . $res->getBody()]);
|
|
}
|
|
}
|
|
|
|
private function _userLoggedIn($url) {
|
|
// Create the user record if it doesn't exist yet
|
|
$user = DB::table('users')->where('url','=',$url)->first();
|
|
if($user) {
|
|
DB::update('UPDATE users SET last_login = ?', [date('Y-m-d H:i:s')]);
|
|
session(['user_id' => $user->id]);
|
|
} else {
|
|
DB::insert('INSERT INTO users (url, created_at, last_login) VALUES(?,?,?)', [$url, date('Y-m-d H:i:s'), date('Y-m-d H:i:s')]);
|
|
$user = DB::select('SELECT *
|
|
FROM users
|
|
WHERE url = ?', [$url]);
|
|
session(['user_id' => $user->id]);
|
|
}
|
|
}
|
|
|
|
public function logout(Request $request) {
|
|
session()->flush();
|
|
return redirect('/');
|
|
}
|
|
|
|
}
|