From 2dcc5105ad0b4f7701099b57f89f45de0d0ff8e8 Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Fri, 4 Sep 2015 17:16:58 +0100
Subject: [PATCH] Added social sign in

---
 app/Exceptions/Handler.php                   |   8 +-
 app/Exceptions/NotifyException.php           |  21 ++
 app/Exceptions/SocialDriverNotConfigured.php |   6 +
 app/Exceptions/UserNotFound.php              |   7 +
 app/Http/Controllers/Auth/AuthController.php | 123 ++++++-
 app/Http/routes.php                          |   4 +
 app/Repos/UserRepo.php                       |  24 ++
 composer.json                                |   3 +-
 composer.lock                                | 327 ++++++++++++++++++-
 config/app.php                               |   4 +-
 config/services.php                          |  18 +-
 resources/assets/js/global.js                |   1 +
 resources/assets/sass/_animations.scss       |  14 +
 resources/assets/sass/_buttons.scss          |   1 -
 resources/views/auth/login.blade.php         |  12 +-
 resources/views/auth/password.blade.php      |   2 +-
 resources/views/auth/reset.blade.php         |   2 +-
 resources/views/base.blade.php               |   2 +-
 resources/views/public.blade.php             |  17 +-
 resources/views/users/form.blade.php         |  14 +-
 20 files changed, 578 insertions(+), 32 deletions(-)
 create mode 100644 app/Exceptions/NotifyException.php
 create mode 100644 app/Exceptions/SocialDriverNotConfigured.php
 create mode 100644 app/Exceptions/UserNotFound.php
 create mode 100644 app/Repos/UserRepo.php

diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php
index 7401affed..d18a9bd2c 100644
--- a/app/Exceptions/Handler.php
+++ b/app/Exceptions/Handler.php
@@ -22,8 +22,7 @@ class Handler extends ExceptionHandler
      *
      * 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)
     {
@@ -39,6 +38,11 @@ class Handler extends ExceptionHandler
      */
     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);
     }
 }
diff --git a/app/Exceptions/NotifyException.php b/app/Exceptions/NotifyException.php
new file mode 100644
index 000000000..c9ff9975d
--- /dev/null
+++ b/app/Exceptions/NotifyException.php
@@ -0,0 +1,21 @@
+<?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
diff --git a/app/Exceptions/SocialDriverNotConfigured.php b/app/Exceptions/SocialDriverNotConfigured.php
new file mode 100644
index 000000000..35fd59e01
--- /dev/null
+++ b/app/Exceptions/SocialDriverNotConfigured.php
@@ -0,0 +1,6 @@
+<?php namespace Oxbow\Exceptions;
+
+
+class SocialDriverNotConfigured extends \Exception
+{
+}
\ No newline at end of file
diff --git a/app/Exceptions/UserNotFound.php b/app/Exceptions/UserNotFound.php
new file mode 100644
index 000000000..26be65119
--- /dev/null
+++ b/app/Exceptions/UserNotFound.php
@@ -0,0 +1,7 @@
+<?php namespace Oxbow\Exceptions;
+
+
+class UserNotFound extends NotifyException
+{
+
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/Auth/AuthController.php b/app/Http/Controllers/Auth/AuthController.php
index 11f68fb98..eacbe2bb9 100644
--- a/app/Http/Controllers/Auth/AuthController.php
+++ b/app/Http/Controllers/Auth/AuthController.php
@@ -2,11 +2,15 @@
 
 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
 {
@@ -27,28 +31,34 @@ 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',
         ]);
     }
@@ -56,15 +66,110 @@ class AuthController extends Controller
     /**
      * 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;
+    }
 }
diff --git a/app/Http/routes.php b/app/Http/routes.php
index 92e416da8..075f49851 100644
--- a/app/Http/routes.php
+++ b/app/Http/routes.php
@@ -82,6 +82,10 @@ Route::group(['middleware' => 'auth'], function () {
 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');
diff --git a/app/Repos/UserRepo.php b/app/Repos/UserRepo.php
new file mode 100644
index 000000000..f6c80ff42
--- /dev/null
+++ b/app/Repos/UserRepo.php
@@ -0,0 +1,24 @@
+<?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
diff --git a/composer.json b/composer.json
index 09d0fffd7..ef5379d46 100644
--- a/composer.json
+++ b/composer.json
@@ -8,7 +8,8 @@
         "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",
diff --git a/composer.lock b/composer.lock
index ec5ebc543..e847bcb4a 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
         "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",
@@ -279,6 +279,214 @@
             ],
             "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",
+                    "email": "mtdowling@gmail.com",
+                    "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",
+                    "email": "mtdowling@gmail.com",
+                    "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",
+                    "email": "mtdowling@gmail.com",
+                    "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",
@@ -672,6 +880,60 @@
             ],
             "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",
+                    "email": "taylorotwell@gmail.com"
+                }
+            ],
+            "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",
@@ -753,6 +1015,69 @@
             ],
             "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",
+                    "email": "bencorlett@me.com",
+                    "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",
diff --git a/config/app.php b/config/app.php
index 80012a3ed..9780558b9 100644
--- a/config/app.php
+++ b/config/app.php
@@ -26,7 +26,7 @@ return [
     |
     */
 
-    'url'             => 'http://localhost',
+    'url'             => env('APP_URL', 'http://localhost'),
 
     /*
     |--------------------------------------------------------------------------
@@ -136,6 +136,7 @@ return [
         Illuminate\Translation\TranslationServiceProvider::class,
         Illuminate\Validation\ValidationServiceProvider::class,
         Illuminate\View\ViewServiceProvider::class,
+        Laravel\Socialite\SocialiteServiceProvider::class,
 
         /**
          * Third Party
@@ -199,6 +200,7 @@ return [
         '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
diff --git a/config/services.php b/config/services.php
index 45603c99a..be9d6eb15 100644
--- a/config/services.php
+++ b/config/services.php
@@ -14,7 +14,7 @@ return [
     |
     */
 
-    'mailgun' => [
+    'mailgun'  => [
         'domain' => '',
         'secret' => '',
     ],
@@ -23,16 +23,28 @@ return [
         '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',
+    ],
+
 ];
diff --git a/resources/assets/js/global.js b/resources/assets/js/global.js
index 41002f4f5..d078946c5 100644
--- a/resources/assets/js/global.js
+++ b/resources/assets/js/global.js
@@ -3,6 +3,7 @@ $(function () {
     // Notification hiding
     $('.notification').click(function () {
         $(this).fadeOut(100);
+
     });
 
     // Dropdown toggles
diff --git a/resources/assets/sass/_animations.scss b/resources/assets/sass/_animations.scss
index f2434b482..e6e85ef8e 100644
--- a/resources/assets/sass/_animations.scss
+++ b/resources/assets/sass/_animations.scss
@@ -42,6 +42,9 @@
   animation-duration: 3s;
   animation-timing-function: ease-in-out;
   animation-fill-mode: forwards;
+  &.stopped {
+    animation-name: notificationStopped;
+  }
 }
 
 @keyframes notification {
@@ -58,6 +61,17 @@
     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 {
diff --git a/resources/assets/sass/_buttons.scss b/resources/assets/sass/_buttons.scss
index 373726c00..19c7b84e4 100644
--- a/resources/assets/sass/_buttons.scss
+++ b/resources/assets/sass/_buttons.scss
@@ -30,7 +30,6 @@ $button-border-radius: 2px;
   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);
 }
diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php
index 5d738a7ad..2c545f281 100644
--- a/resources/views/auth/login.blade.php
+++ b/resources/views/auth/login.blade.php
@@ -1,6 +1,6 @@
 @extends('public')
 
-@section('sidebar')
+@section('content')
 
     <div class="center-box">
         <h1>Log In</h1>
@@ -23,6 +23,16 @@
                 <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
diff --git a/resources/views/auth/password.blade.php b/resources/views/auth/password.blade.php
index 6b9f46ec7..d3ea08e1c 100644
--- a/resources/views/auth/password.blade.php
+++ b/resources/views/auth/password.blade.php
@@ -2,7 +2,7 @@
 
 @section('body-class', 'image-cover login')
 
-@section('sidebar')
+@section('content')
 
 
     <div class="text-center">
diff --git a/resources/views/auth/reset.blade.php b/resources/views/auth/reset.blade.php
index 6762ef2f0..c440f64e7 100644
--- a/resources/views/auth/reset.blade.php
+++ b/resources/views/auth/reset.blade.php
@@ -2,7 +2,7 @@
 
 @section('body-class', 'image-cover login')
 
-@section('sidebar')
+@section('content')
 
 
     <div class="text-center">
diff --git a/resources/views/base.blade.php b/resources/views/base.blade.php
index 76447b5f5..4dc3251ca 100644
--- a/resources/views/base.blade.php
+++ b/resources/views/base.blade.php
@@ -31,7 +31,7 @@
     @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
diff --git a/resources/views/public.blade.php b/resources/views/public.blade.php
index f311aefae..b11971104 100644
--- a/resources/views/public.blade.php
+++ b/resources/views/public.blade.php
@@ -6,14 +6,25 @@
     <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')
diff --git a/resources/views/users/form.blade.php b/resources/views/users/form.blade.php
index 5e55145fd..05c4ac02b 100644
--- a/resources/views/users/form.blade.php
+++ b/resources/views/users/form.blade.php
@@ -8,6 +8,13 @@
     @include('form/text', ['name' => 'email'])
 </div>
 
+@if($currentUser->can('user-update'))
+    <div class="form-group">
+        <label for="role">User Role</label>
+        @include('form.role-select', ['name' => 'role', 'options' => \Oxbow\Role::all(), 'displayKey' => 'display_name'])
+    </div>
+@endif
+
 @if(isset($model))
     <div class="form-group">
         <span class="text-muted">
@@ -16,13 +23,6 @@
     </div>
 @endif
 
-@if($currentUser->can('user-update'))
-    <div class="form-group">
-        <label for="role">User Role</label>
-        @include('form.role-select', ['name' => 'role', 'options' => \Oxbow\Role::all(), 'displayKey' => 'display_name'])
-    </div>
-@endif
-
 <div class="form-group">
     <label for="password">Password</label>
     @include('form/password', ['name' => 'password'])