*
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
*
- * @param \Exception $e
- * @return void
+ * @param \Exception $e
*/
public function report(Exception $e)
{
*/
public function render($request, Exception $e)
{
+ if($e instanceof NotifyException) {
+ \Session::flash('error', $e->message);
+ return response()->redirectTo($e->redirectLocation);
+ }
+
return parent::render($request, $e);
}
}
--- /dev/null
+<?php namespace Oxbow\Exceptions;
+
+
+class NotifyException extends \Exception
+{
+
+ public $message;
+ public $redirectLocation;
+
+ /**
+ * NotifyException constructor.
+ * @param string $message
+ * @param string $redirectLocation
+ */
+ public function __construct($message, $redirectLocation)
+ {
+ $this->message = $message;
+ $this->redirectLocation = $redirectLocation;
+ parent::__construct();
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php namespace Oxbow\Exceptions;
+
+
+class SocialDriverNotConfigured extends \Exception
+{
+}
\ No newline at end of file
--- /dev/null
+<?php namespace Oxbow\Exceptions;
+
+
+class UserNotFound extends NotifyException
+{
+
+}
\ No newline at end of file
namespace Oxbow\Http\Controllers\Auth;
+use Oxbow\Exceptions\SocialDriverNotConfigured;
+use Oxbow\Exceptions\UserNotFound;
+use Oxbow\Repos\UserRepo;
use Oxbow\User;
use Validator;
use Oxbow\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
+use Laravel\Socialite\Contracts\Factory as Socialite;
class AuthController extends Controller
{
protected $redirectPath = '/';
protected $redirectAfterLogout = '/login';
+ protected $validSocialDrivers = ['google', 'github'];
+
+ protected $socialite;
+ protected $userRepo;
/**
* Create a new authentication controller instance.
- *
- * @return void
+ * @param Socialite $socialite
+ * @param UserRepo $userRepo
*/
- public function __construct()
+ public function __construct(Socialite $socialite, UserRepo $userRepo)
{
$this->middleware('guest', ['except' => 'getLogout']);
+ $this->socialite = $socialite;
+ $this->userRepo = $userRepo;
}
/**
* Get a validator for an incoming registration request.
*
- * @param array $data
+ * @param array $data
* @return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
- 'name' => 'required|max:255',
- 'email' => 'required|email|max:255|unique:users',
+ 'name' => 'required|max:255',
+ 'email' => 'required|email|max:255|unique:users',
'password' => 'required|confirmed|min:6',
]);
}
/**
* Create a new user instance after a valid registration.
*
- * @param array $data
+ * @param array $data
* @return User
*/
protected function create(array $data)
{
return User::create([
- 'name' => $data['name'],
- 'email' => $data['email'],
+ 'name' => $data['name'],
+ 'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
}
+
+ /**
+ * Show the application login form.
+ *
+ * @return \Illuminate\Http\Response
+ */
+ public function getLogin()
+ {
+
+ if (view()->exists('auth.authenticate')) {
+ return view('auth.authenticate');
+ }
+
+ $socialDrivers = $this->getActiveSocialDrivers();
+
+ return view('auth.login', ['socialDrivers' => $socialDrivers]);
+ }
+
+ /**
+ * Redirect to the relevant social site.
+ * @param $socialDriver
+ * @return \Symfony\Component\HttpFoundation\RedirectResponse
+ */
+ public function getSocialLogin($socialDriver)
+ {
+ $driver = $this->validateSocialDriver($socialDriver);
+ return $this->socialite->driver($driver)->redirect();
+ }
+
+ /**
+ * The callback for social login services.
+ *
+ * @param $socialDriver
+ * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
+ * @throws UserNotFound
+ */
+ public function socialCallback($socialDriver)
+ {
+ $driver = $this->validateSocialDriver($socialDriver);
+ // Get user details from social driver
+ $socialUser = $this->socialite->driver($driver)->user();
+ $user = $this->userRepo->getByEmail($socialUser->getEmail());
+
+ // Redirect if the email is not a current user.
+ if ($user === null) {
+ throw new UserNotFound('A user with the email ' . $socialUser->getEmail() . ' was not found.', '/login');
+ }
+
+ \Auth::login($user, true);
+ return redirect($this->redirectPath);
+ }
+
+ /**
+ * Ensure the social driver is correct and supported.
+ *
+ * @param $socialDriver
+ * @return string
+ * @throws SocialDriverNotConfigured
+ */
+ protected function validateSocialDriver($socialDriver)
+ {
+ $driver = trim(strtolower($socialDriver));
+
+ if (!in_array($driver, $this->validSocialDrivers)) abort(404, 'Social Driver Not Found');
+ if(!$this->checkSocialDriverConfigured($driver)) throw new SocialDriverNotConfigured;
+
+ return $driver;
+ }
+
+ /**
+ * Check a social driver has been configured correctly.
+ * @param $driver
+ * @return bool
+ */
+ protected function checkSocialDriverConfigured($driver)
+ {
+ $upperName = strtoupper($driver);
+ $config = [env($upperName . '_APP_ID', false), env($upperName . '_APP_SECRET', false), env('APP_URL', false)];
+ return (!in_array(false, $config) && !in_array(null, $config));
+ }
+
+ /**
+ * Gets the names of the active social drivers.
+ * @return array
+ */
+ protected function getActiveSocialDrivers()
+ {
+ $activeDrivers = [];
+ foreach($this->validSocialDrivers as $driverName) {
+ if($this->checkSocialDriverConfigured($driverName)) {
+ $activeDrivers[$driverName] = true;
+ }
+ }
+ return $activeDrivers;
+ }
}
Route::get('/login', 'Auth\AuthController@getLogin');
Route::post('/login', 'Auth\AuthController@postLogin');
Route::get('/logout', 'Auth\AuthController@getLogout');
+// Login using social authentication
+Route::get('/login/service/{socialService}', 'Auth\AuthController@getSocialLogin');
+Route::get('/login/service/{socialService}/callback', 'Auth\AuthController@socialCallback');
+
// Password reset link request routes...
Route::get('/password/email', 'Auth\PasswordController@getEmail');
Route::post('/password/email', 'Auth\PasswordController@postEmail');
--- /dev/null
+<?php namespace Oxbow\Repos;
+
+
+use Oxbow\User;
+
+class UserRepo
+{
+
+ protected $user;
+
+ /**
+ * UserRepo constructor.
+ * @param $user
+ */
+ public function __construct(User $user)
+ {
+ $this->user = $user;
+ }
+
+
+ public function getByEmail($email) {
+ return $this->user->where('email', '=', $email)->first();
+ }
+}
\ No newline at end of file
"php": ">=5.5.9",
"laravel/framework": "5.1.*",
"intervention/image": "^2.3",
- "barryvdh/laravel-ide-helper": "^2.1"
+ "barryvdh/laravel-ide-helper": "^2.1",
+ "laravel/socialite": "^2.0"
},
"require-dev": {
"fzaninotto/faker": "~1.4",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "hash": "16de3a44150d9425a501c9873cb28eaf",
+ "hash": "7d7e80e9f1c13417c35195f79e41c038",
"packages": [
{
"name": "barryvdh/laravel-ide-helper",
],
"time": "2014-12-20 21:24:13"
},
+ {
+ "name": "guzzle/guzzle",
+ "version": "v3.9.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/guzzle3.git",
+ "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/guzzle3/zipball/0645b70d953bc1c067bbc8d5bc53194706b628d9",
+ "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9",
+ "shasum": ""
+ },
+ "require": {
+ "ext-curl": "*",
+ "php": ">=5.3.3",
+ "symfony/event-dispatcher": "~2.1"
+ },
+ "replace": {
+ "guzzle/batch": "self.version",
+ "guzzle/cache": "self.version",
+ "guzzle/common": "self.version",
+ "guzzle/http": "self.version",
+ "guzzle/inflection": "self.version",
+ "guzzle/iterator": "self.version",
+ "guzzle/log": "self.version",
+ "guzzle/parser": "self.version",
+ "guzzle/plugin": "self.version",
+ "guzzle/plugin-async": "self.version",
+ "guzzle/plugin-backoff": "self.version",
+ "guzzle/plugin-cache": "self.version",
+ "guzzle/plugin-cookie": "self.version",
+ "guzzle/plugin-curlauth": "self.version",
+ "guzzle/plugin-error-response": "self.version",
+ "guzzle/plugin-history": "self.version",
+ "guzzle/plugin-log": "self.version",
+ "guzzle/plugin-md5": "self.version",
+ "guzzle/plugin-mock": "self.version",
+ "guzzle/plugin-oauth": "self.version",
+ "guzzle/service": "self.version",
+ "guzzle/stream": "self.version"
+ },
+ "require-dev": {
+ "doctrine/cache": "~1.3",
+ "monolog/monolog": "~1.0",
+ "phpunit/phpunit": "3.7.*",
+ "psr/log": "~1.0",
+ "symfony/class-loader": "~2.1",
+ "zendframework/zend-cache": "2.*,<2.3",
+ "zendframework/zend-log": "2.*,<2.3"
+ },
+ "suggest": {
+ "guzzlehttp/guzzle": "Guzzle 5 has moved to a new package name. The package you have installed, Guzzle 3, is deprecated."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.9-dev"
+ }
+ },
+ "autoload": {
+ "psr-0": {
+ "Guzzle": "src/",
+ "Guzzle\\Tests": "tests/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Guzzle Community",
+ "homepage": "https://github.com/guzzle/guzzle/contributors"
+ }
+ ],
+ "description": "PHP HTTP client. This library is deprecated in favor of https://packagist.org/packages/guzzlehttp/guzzle",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": [
+ "client",
+ "curl",
+ "framework",
+ "http",
+ "http client",
+ "rest",
+ "web service"
+ ],
+ "time": "2015-03-18 18:23:50"
+ },
+ {
+ "name": "guzzlehttp/guzzle",
+ "version": "6.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/guzzle.git",
+ "reference": "a8dfeff00eb84616a17fea7a4d72af35e750410f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/guzzle/zipball/a8dfeff00eb84616a17fea7a4d72af35e750410f",
+ "reference": "a8dfeff00eb84616a17fea7a4d72af35e750410f",
+ "shasum": ""
+ },
+ "require": {
+ "guzzlehttp/promises": "~1.0",
+ "guzzlehttp/psr7": "~1.1",
+ "php": ">=5.5.0"
+ },
+ "require-dev": {
+ "ext-curl": "*",
+ "phpunit/phpunit": "~4.0",
+ "psr/log": "~1.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/functions_include.php"
+ ],
+ "psr-4": {
+ "GuzzleHttp\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "description": "Guzzle is a PHP HTTP client library",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": [
+ "client",
+ "curl",
+ "framework",
+ "http",
+ "http client",
+ "rest",
+ "web service"
+ ],
+ "time": "2015-07-04 20:09:24"
+ },
+ {
+ "name": "guzzlehttp/promises",
+ "version": "1.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/promises.git",
+ "reference": "97fe7210def29451ec74923b27e552238defd75a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/promises/zipball/97fe7210def29451ec74923b27e552238defd75a",
+ "reference": "97fe7210def29451ec74923b27e552238defd75a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.5.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Promise\\": "src/"
+ },
+ "files": [
+ "src/functions_include.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "description": "Guzzle promises library",
+ "keywords": [
+ "promise"
+ ],
+ "time": "2015-08-15 19:37:21"
+ },
{
"name": "guzzlehttp/psr7",
"version": "1.2.0",
],
"time": "2015-08-12 18:16:08"
},
+ {
+ "name": "laravel/socialite",
+ "version": "v2.0.12",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laravel/socialite.git",
+ "reference": "0bb08c8666f4c01e55e3b3b0e42f2b5075be6a6e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laravel/socialite/zipball/0bb08c8666f4c01e55e3b3b0e42f2b5075be6a6e",
+ "reference": "0bb08c8666f4c01e55e3b3b0e42f2b5075be6a6e",
+ "shasum": ""
+ },
+ "require": {
+ "guzzlehttp/guzzle": "~5.0|~6.0",
+ "illuminate/contracts": "~5.0",
+ "illuminate/http": "~5.0",
+ "illuminate/support": "~5.0",
+ "league/oauth1-client": "~1.0",
+ "php": ">=5.4.0"
+ },
+ "require-dev": {
+ "mockery/mockery": "~0.9",
+ "phpunit/phpunit": "~4.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Laravel\\Socialite\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ }
+ ],
+ "description": "Laravel wrapper around OAuth 1 & OAuth 2 libraries.",
+ "keywords": [
+ "laravel",
+ "oauth"
+ ],
+ "time": "2015-08-30 01:12:56"
+ },
{
"name": "league/flysystem",
"version": "1.0.11",
],
"time": "2015-07-28 20:41:58"
},
+ {
+ "name": "league/oauth1-client",
+ "version": "1.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/oauth1-client.git",
+ "reference": "4d4edd9b6014f882e319231a9b3351e3a1dfdc81"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/4d4edd9b6014f882e319231a9b3351e3a1dfdc81",
+ "reference": "4d4edd9b6014f882e319231a9b3351e3a1dfdc81",
+ "shasum": ""
+ },
+ "require": {
+ "guzzle/guzzle": "3.*",
+ "php": ">=5.3.0"
+ },
+ "require-dev": {
+ "mockery/mockery": "~0.9",
+ "phpunit/phpunit": "~4.0",
+ "squizlabs/php_codesniffer": "~2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "League\\OAuth1\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ben Corlett",
+ "homepage": "http://www.webcomm.com.au",
+ "role": "Developer"
+ }
+ ],
+ "description": "OAuth 1.0 Client Library",
+ "keywords": [
+ "Authentication",
+ "SSO",
+ "authorization",
+ "bitbucket",
+ "identity",
+ "idp",
+ "oauth",
+ "oauth1",
+ "single sign on",
+ "trello",
+ "tumblr",
+ "twitter"
+ ],
+ "time": "2015-08-22 09:49:14"
+ },
{
"name": "monolog/monolog",
"version": "1.16.0",
|
*/
- 'url' => 'http://localhost',
+ 'url' => env('APP_URL', 'http://localhost'),
/*
|--------------------------------------------------------------------------
Illuminate\Translation\TranslationServiceProvider::class,
Illuminate\Validation\ValidationServiceProvider::class,
Illuminate\View\ViewServiceProvider::class,
+ Laravel\Socialite\SocialiteServiceProvider::class,
/**
* Third Party
'URL' => Illuminate\Support\Facades\URL::class,
'Validator' => Illuminate\Support\Facades\Validator::class,
'View' => Illuminate\Support\Facades\View::class,
+ 'Socialite' => Laravel\Socialite\Facades\Socialite::class,
/**
* Third Party
|
*/
- 'mailgun' => [
+ 'mailgun' => [
'domain' => '',
'secret' => '',
],
'secret' => '',
],
- 'ses' => [
+ 'ses' => [
'key' => '',
'secret' => '',
'region' => 'us-east-1',
],
- 'stripe' => [
+ 'stripe' => [
'model' => Oxbow\User::class,
'key' => '',
'secret' => '',
],
+ 'github' => [
+ 'client_id' => env('GITHUB_APP_ID', false),
+ 'client_secret' => env('GITHUB_APP_SECRET', false),
+ 'redirect' => env('APP_URL') . '/login/service/github/callback',
+ ],
+
+ 'google' => [
+ 'client_id' => env('GOOGLE_APP_ID', false),
+ 'client_secret' => env('GOOGLE_APP_SECRET', false),
+ 'redirect' => env('APP_URL') . '/login/service/google/callback',
+ ],
+
];
// Notification hiding
$('.notification').click(function () {
$(this).fadeOut(100);
+
});
// Dropdown toggles
animation-duration: 3s;
animation-timing-function: ease-in-out;
animation-fill-mode: forwards;
+ &.stopped {
+ animation-name: notificationStopped;
+ }
}
@keyframes notification {
transform: translate3d(580px, 0, 0);
}
}
+@keyframes notificationStopped {
+ 0% {
+ transform: translate3d(580px, 0, 0);
+ }
+ 10% {
+ transform: translate3d(0, 0, 0);
+ }
+ 100% {
+ transform: translate3d(0, 0, 0);
+ }
+}
@keyframes menuIn {
from {
border-radius: $button-border-radius;
cursor: pointer;
transition: all ease-in-out 120ms;
- text-transform: uppercase;
box-shadow: 0 0.5px 1.5px 0 rgba(0, 0, 0, 0.21);
@include generate-button-colors(#EEE, $primary);
}
@extends('public')
-@section('sidebar')
+@section('content')
<div class="center-box">
<h1>Log In</h1>
<button class="button block pos">Sign In</button>
</div>
</form>
+ @if(count($socialDrivers) > 0)
+ <hr class="margin-top">
+ <h3 class="text-muted">Social Login</h3>
+ @if(isset($socialDrivers['google']))
+ <a href="/login/service/google" style="color: #DC4E41;"><i class="zmdi zmdi-google-plus-box zmdi-hc-4x"></i></a>
+ @endif
+ @if(isset($socialDrivers['github']))
+ <a href="/login/service/github" style="color:#000;"><i class="zmdi zmdi-github zmdi-hc-4x"></i></a>
+ @endif
+ @endif
</div>
@stop
\ No newline at end of file
@section('body-class', 'image-cover login')
-@section('sidebar')
+@section('content')
<div class="text-center">
@section('body-class', 'image-cover login')
-@section('sidebar')
+@section('content')
<div class="text-center">
@endif
@if(Session::has('error'))
- <div class="notification anim neg">
+ <div class="notification anim neg stopped">
<i class="zmdi zmdi-alert-circle"></i> <span>{{ Session::get('error') }}</span>
</div>
@endif
<link rel="stylesheet" href="/css/app.css">
<link href='//fonts.googleapis.com/css?family=Roboto:400,400italic,500,500italic,700,700italic,300italic,100,300' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="/bower/material-design-iconic-font/dist/css/material-design-iconic-font.min.css">
+
+ <!-- Scripts -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
+ <script src="/js/common.js"></script>
</head>
<body class="@yield('body-class')">
-<section id="sidebar">
- @yield('sidebar')
-</section>
+@if(Session::has('success'))
+ <div class="notification anim pos">
+ <i class="zmdi zmdi-mood"></i> <span>{{ Session::get('success') }}</span>
+ </div>
+@endif
+
+@if(Session::has('error'))
+ <div class="notification anim neg stopped">
+ <i class="zmdi zmdi-alert-circle"></i> <span>{{ Session::get('error') }}</span>
+ </div>
+@endif
<section class="container">
@yield('content')
@include('form/text', ['name' => 'email'])
</div>
-@if(isset($model))
+@if($currentUser->can('user-update'))
<div class="form-group">
- <span class="text-muted">
- Only fill the below if you would like <br>to change your password:
- </span>
+ <label for="role">User Role</label>
+ @include('form.role-select', ['name' => 'role', 'options' => \Oxbow\Role::all(), 'displayKey' => 'display_name'])
</div>
@endif
-@if($currentUser->can('user-update'))
+@if(isset($model))
<div class="form-group">
- <label for="role">User Role</label>
- @include('form.role-select', ['name' => 'role', 'options' => \Oxbow\Role::all(), 'displayKey' => 'display_name'])
+ <span class="text-muted">
+ Only fill the below if you would like <br>to change your password:
+ </span>
</div>
@endif