├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── public ├── .gitkeep └── css │ ├── socialbuttons.css │ └── socialbuttons.less ├── src ├── Mmanos │ └── Social │ │ ├── Facades │ │ └── Social.php │ │ ├── Social.php │ │ ├── SocialServiceProvider.php │ │ └── SocialTrait.php ├── config │ └── config.php ├── controllers │ └── SocialController.php ├── lang │ └── .gitkeep ├── migrations │ └── 2014_09_04_000000_laravel_social_create_providers_table.php ├── models │ └── Provider.php └── views │ └── social │ └── complete.blade.php └── tests └── .gitkeep /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.phar 3 | composer.lock 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Mark Manos 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Social Login Package for Laravel 4 2 | 3 | This package is a Laravel 4 service provider for [Lusitanian/PHPoAuthLib](https://github.com/Lusitanian/PHPoAuthLib) which provides oAuth support in PHP 5.3+ and is very easy to integrate with any project which requires an oAuth client. 4 | 5 | In addition, you may take advantage of the optional controller and model to make it very easy to: 6 | 7 | * Log in with a social provider 8 | * Connect an existing user record with a social provider 9 | * Perform requests against the social provider API with each user's unique access token 10 | 11 | ## Supported Services 12 | 13 | See the documentation for Lusitanian/PHPoAuthLib for the [list of supported services](https://github.com/Lusitanian/PHPoAuthLib#included-service-implementations). 14 | 15 | ## Installation Via Composer 16 | 17 | Add this to your composer.json file, in the require object: 18 | 19 | ```javascript 20 | "mmanos/laravel-social": "dev-master" 21 | ``` 22 | 23 | After that, run composer install to install the package. 24 | 25 | Add the service provider to `app/config/app.php`, within the `providers` array. 26 | 27 | ```php 28 | 'providers' => array( 29 | // ... 30 | 'Mmanos\Social\SocialServiceProvider', 31 | ) 32 | ``` 33 | 34 | Add a class alias to `app/config/app.php`, within the `aliases` array. 35 | 36 | ```php 37 | 'aliases' => array( 38 | // ... 39 | 'Social' => 'Mmanos\Social\Facades\Social', 40 | ) 41 | ``` 42 | 43 | ## Configuration 44 | 45 | Publish the default config file to your application so you can make modifications. 46 | 47 | ```console 48 | $ php artisan config:publish mmanos/laravel-social 49 | ``` 50 | 51 | Add your service provider credentials to the published config file: `app/config/packages/mmanos/laravel-social/config.php` 52 | 53 | ## Basic Usage 54 | 55 | Obtain a service class object for a provider. 56 | 57 | ```php 58 | $service = Social::service('facebook'); 59 | ``` 60 | 61 | Optionally, add a second parameter with the URL which the service needs to redirect to, otherwise it will redirect to the current URL. 62 | 63 | ```php 64 | $service = Social::service('facebook', 'http://example.com'); 65 | ``` 66 | 67 | Redirect the user to the oAuth log in page for a provider. 68 | 69 | ```php 70 | return Redirect::to((string) $service->getAuthorizationUri()); 71 | ``` 72 | 73 | For examples of how to integrate with a specific provider, [see here](https://github.com/Lusitanian/PHPoAuthLib/tree/master/examples). 74 | 75 | ## Using The Provided Controller And Model 76 | 77 | For added convenience, use the built-in controller and model to easily log in or connect with a social provider account. 78 | 79 | #### Requirements 80 | 81 | This implementation is fairly opinionated and assumes that you want to allow your users to log in or sign up seamlessly with their existing social provider account and associate that social provider account with an existing user record. 82 | 83 | It has these requirements: 84 | 85 | * You have an existing user record with a numeric primary key 86 | * You are using the Laravel authentication system 87 | * You have the Laravel session system enabled 88 | * You have an app key defined in your config/app.php file so the Crypt class can be used to encrypt the access tokens 89 | 90 | #### Migration 91 | 92 | Publish and run the database migrations for this package. This will create a `user_providers` table. 93 | 94 | ```console 95 | $ php artisan migrate:publish mmanos/laravel-social 96 | $ php artisan migrate 97 | ``` 98 | 99 | > **Note:** You can change the default table name used to store the user provider information by changing the value of the `table` key in the config file. 100 | 101 | #### Model Setup 102 | 103 | Add the SocialTrait to your User model definition. 104 | 105 | ```php 106 | use Mmanos\Social\SocialTrait; 107 | 108 | class User extends Eloquent 109 | { 110 | use SocialTrait; 111 | 112 | } 113 | ``` 114 | 115 | #### Social Login Flow 116 | 117 | Simply create a link to the built-in controller to initiate a log in flow. The user will be redirected to the provider login page before they return to your website. 118 | 119 | If an existing user is already linked to the provider account, they will be logged in as that user. 120 | 121 | If an existing user is not found for the provider account, a new user record will be created and then a link to the provider account will be made before they are logged in as that user. 122 | 123 | ```php 124 | 125 | Log in with Twitter 126 | 127 | ``` 128 | 129 | To customize where the user is redirected to after the log in flow, add `onsuccess` and `onerror` parameters. 130 | 131 | ```php 132 | 133 | Log in with Twitter 134 | 135 | ``` 136 | 137 | > **Note:** The default redirect location is to the `referer` header. Otherwise `/`. 138 | 139 | #### Connecting A Social Account Flow 140 | 141 | You can also associate a social provider account to an existing user if they are already logged in. 142 | 143 | ```php 144 | 145 | Connect Your Twitter Account 146 | 147 | ``` 148 | 149 | > **Note:** This action also supports the `onsuccess` and `onerror` parameters. 150 | 151 | #### Working With Users And Providers 152 | 153 | You can fetch all providers linked to a user. 154 | 155 | ```php 156 | $providers = Auth::user()->providers; 157 | ``` 158 | 159 | You can check to see if a user is connected to a given provider. 160 | 161 | ```php 162 | if (Auth::user()->hasProvider('twitter')) { 163 | // 164 | } 165 | ``` 166 | 167 | You can fetch a single provider instance for a user. 168 | 169 | ```php 170 | $provider = Auth::user()->provider('twitter'); 171 | ``` 172 | 173 | This package stores an encrypted version of the access_token for each user provider. That way you can easily make API calls to the provider service on behalf of the user. 174 | 175 | ```php 176 | $result = Auth::user()->provider('twitter')->request('account/verify_credentials.json'); 177 | ``` 178 | 179 | > **Note:** Keep in mind it is possible for an access_token to expire. You can refresh a user's access_token by initiating a redirect to the `getConnect` action in the controller. 180 | 181 | #### Create User Logic 182 | 183 | If you want to customize the logic used to create a user during the social login flow, modify the `create_user` key in the config file. 184 | 185 | ```php 186 | 'create_user' => function ($data) { 187 | $user = new User; 188 | $user->email = array_get($data, 'email'); 189 | $user->password = Hash::make(Str::random()); 190 | $user->location = array_get($data, 'location'); // Only passed by certain providers. 191 | $user->save(); 192 | 193 | return $user->id; 194 | }, 195 | ``` 196 | 197 | > **Note:** Make sure to return a numeric primary key when finished. 198 | 199 | > **Note:** The information passed in the `$data` parameter is the data returned from the `fetch_user_info` functions for each provider in the config file. 200 | 201 | #### Provider User Information 202 | 203 | To customize which data you want to retrieve from a social provider account, modify the `fetch_user_info` key for each provider in the config file. 204 | 205 | ```php 206 | 'fetch_user_info' => function ($service) { 207 | $result = json_decode($service->request('account/verify_credentials.json'), true); 208 | return array( 209 | 'id' => array_get($result, 'id'), 210 | 'email' => null, 211 | 'first_name' => array_get(explode(' ', array_get($result, 'name')), 0), 212 | 'last_name' => array_get(explode(' ', array_get($result, 'name')), 1) 213 | 'location' => array_get($result, 'location'), 214 | ); 215 | }, 216 | ``` 217 | 218 | > **Note:** This function also allows you to normalize the user data returned by each of the social providers by mapping their fields to fields you want to store in your user record. 219 | 220 | #### New User Validation 221 | 222 | You can configure the social login flow to validate the user information returned by the social provider. This way you can ensure that all of the data you require for a new user is properly obtained and in the correct format. 223 | 224 | To customize the validation rules, modify the `user_validation` key in the config file. 225 | 226 | ```php 227 | 'user_validation' => array( 228 | 'email' => 'required|email', 229 | 'first_name' => 'required', 230 | 'location' => 'required', 231 | ), 232 | ``` 233 | 234 | > **Note:** You may also declare a closure function to create and return a Validator instance for greater flexibility. 235 | 236 | ## Social Buttons 237 | 238 | This package provides some convenient css that allows you to create nice social buttons. It is built for use with the [Twitter Bootstrap](https://github.com/twbs/bootstrap) and [Font Awesome](http://fontawesome.io/) frameworks. 239 | 240 | #### Publish The Assets 241 | 242 | Publish the public assets for this package. 243 | 244 | ```console 245 | $ php artisan asset:publish mmanos/laravel-social 246 | ``` 247 | 248 | #### Include The Assets 249 | 250 | ```php 251 | {{ HTML::style('/packages/mmanos/laravel-social/css/socialbuttons.css') }} 252 | ``` 253 | 254 | #### Building Buttons 255 | 256 | ```php 257 | 258 | Log in with Twitter 259 | 260 | 261 | 262 | 263 | ``` 264 | 265 | [See here](https://github.com/lipis/bootstrap-social#available-classes) for a list of supported class names. 266 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mmanos/laravel-social", 3 | "description": "A social login package for Laravel 4.", 4 | "keywords": ["laravel", "social", "login", "providers", "oauth"], 5 | "authors": [ 6 | { 7 | "name": "Mark Manos", 8 | "email": "mark@airpac.com" 9 | } 10 | ], 11 | "require": { 12 | "php": ">=5.4.0", 13 | "illuminate/support": ">=4.1", 14 | "lusitanian/oauth": "~0.3" 15 | }, 16 | "autoload": { 17 | "classmap": [ 18 | "src/migrations", 19 | "src/controllers", 20 | "src/models" 21 | ], 22 | "psr-0": { 23 | "Mmanos\\Social\\": "src/" 24 | } 25 | }, 26 | "minimum-stability": "stable", 27 | "license": "MIT" 28 | } 29 | -------------------------------------------------------------------------------- /public/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmanos/laravel-social/f3ec7165509825d16cb9d06759a77580a291cb83/public/.gitkeep -------------------------------------------------------------------------------- /public/css/socialbuttons.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Social Buttons for Bootstrap 3 | * 4 | * Copyright 2013-2014 Panayiotis Lipiridis 5 | * Licensed under the MIT License 6 | * 7 | * https://github.com/lipis/bootstrap-social 8 | */ 9 | 10 | .btn-social { 11 | position: relative; 12 | padding-left: 55px; 13 | padding-top: 10px; 14 | padding-bottom: 10px; 15 | text-align: left; 16 | font-weight: bold; 17 | white-space: nowrap; 18 | overflow: hidden; 19 | text-overflow: ellipsis; 20 | } 21 | .btn-social :first-child { 22 | position: absolute; 23 | left: 0; 24 | top: 0; 25 | bottom: 0; 26 | width: 40px; 27 | line-height: 42px; 28 | font-size: 1.6em; 29 | text-align: center; 30 | border-right: 1px solid rgba(0, 0, 0, 0.15); 31 | } 32 | .btn-social.btn-lg { 33 | padding-left: 61px; 34 | padding-top: 10px; 35 | padding-bottom: 10px; 36 | } 37 | .btn-social.btn-lg :first-child { 38 | line-height: 45px; 39 | width: 45px; 40 | font-size: 1.8em; 41 | } 42 | .btn-social.btn-sm { 43 | padding-left: 38px; 44 | padding-top: 5px; 45 | padding-bottom: 5px; 46 | } 47 | .btn-social.btn-sm :first-child { 48 | line-height: 28px; 49 | width: 28px; 50 | font-size: 1.4em; 51 | } 52 | .btn-social.btn-xs { 53 | padding-left: 30px; 54 | } 55 | .btn-social.btn-xs :first-child { 56 | line-height: 20px; 57 | width: 20px; 58 | font-size: 1.2em; 59 | } 60 | .btn-social-icon { 61 | position: relative; 62 | padding-left: 55px; 63 | padding-top: 10px; 64 | padding-bottom: 10px; 65 | text-align: left; 66 | font-weight: bold; 67 | white-space: nowrap; 68 | overflow: hidden; 69 | text-overflow: ellipsis; 70 | height: 42px; 71 | width: 42px; 72 | padding: 0; 73 | } 74 | .btn-social-icon :first-child { 75 | position: absolute; 76 | left: 0; 77 | top: 0; 78 | bottom: 0; 79 | width: 40px; 80 | line-height: 42px; 81 | font-size: 1.6em; 82 | text-align: center; 83 | border-right: 1px solid rgba(0, 0, 0, 0.15); 84 | } 85 | .btn-social-icon.btn-lg { 86 | padding-left: 61px; 87 | padding-top: 10px; 88 | padding-bottom: 10px; 89 | } 90 | .btn-social-icon.btn-lg :first-child { 91 | line-height: 45px; 92 | width: 45px; 93 | font-size: 1.8em; 94 | } 95 | .btn-social-icon.btn-sm { 96 | padding-left: 38px; 97 | padding-top: 5px; 98 | padding-bottom: 5px; 99 | } 100 | .btn-social-icon.btn-sm :first-child { 101 | line-height: 28px; 102 | width: 28px; 103 | font-size: 1.4em; 104 | } 105 | .btn-social-icon.btn-xs { 106 | padding-left: 30px; 107 | } 108 | .btn-social-icon.btn-xs :first-child { 109 | line-height: 20px; 110 | width: 20px; 111 | font-size: 1.2em; 112 | } 113 | .btn-social-icon :first-child { 114 | border: none; 115 | text-align: center; 116 | width: 100% !important; 117 | } 118 | .btn-social-icon.btn-lg { 119 | height: 45px; 120 | width: 45px; 121 | padding-left: 0; 122 | padding-right: 0; 123 | } 124 | .btn-social-icon.btn-sm { 125 | height: 30px; 126 | width: 30px; 127 | padding-left: 0; 128 | padding-right: 0; 129 | } 130 | .btn-social-icon.btn-xs { 131 | height: 22px; 132 | width: 22px; 133 | padding-left: 0; 134 | padding-right: 0; 135 | } 136 | .btn-adn { 137 | color: #ffffff; 138 | background-color: #d87a68; 139 | border-color: rgba(0, 0, 0, 0.2); 140 | } 141 | .btn-adn:hover, 142 | .btn-adn:focus, 143 | .btn-adn:active, 144 | .btn-adn.active, 145 | .open > .dropdown-toggle.btn-adn { 146 | color: #ffffff; 147 | background-color: #ce563f; 148 | border-color: rgba(0, 0, 0, 0.2); 149 | } 150 | .btn-adn:active, 151 | .btn-adn.active, 152 | .open > .dropdown-toggle.btn-adn { 153 | background-image: none; 154 | } 155 | .btn-adn.disabled, 156 | .btn-adn[disabled], 157 | fieldset[disabled] .btn-adn, 158 | .btn-adn.disabled:hover, 159 | .btn-adn[disabled]:hover, 160 | fieldset[disabled] .btn-adn:hover, 161 | .btn-adn.disabled:focus, 162 | .btn-adn[disabled]:focus, 163 | fieldset[disabled] .btn-adn:focus, 164 | .btn-adn.disabled:active, 165 | .btn-adn[disabled]:active, 166 | fieldset[disabled] .btn-adn:active, 167 | .btn-adn.disabled.active, 168 | .btn-adn[disabled].active, 169 | fieldset[disabled] .btn-adn.active { 170 | background-color: #d87a68; 171 | border-color: rgba(0, 0, 0, 0.2); 172 | } 173 | .btn-adn .badge { 174 | color: #d87a68; 175 | background-color: #ffffff; 176 | } 177 | .btn-bitbucket { 178 | color: #ffffff; 179 | background-color: #205081; 180 | border-color: rgba(0, 0, 0, 0.2); 181 | } 182 | .btn-bitbucket:hover, 183 | .btn-bitbucket:focus, 184 | .btn-bitbucket:active, 185 | .btn-bitbucket.active, 186 | .open > .dropdown-toggle.btn-bitbucket { 187 | color: #ffffff; 188 | background-color: #163758; 189 | border-color: rgba(0, 0, 0, 0.2); 190 | } 191 | .btn-bitbucket:active, 192 | .btn-bitbucket.active, 193 | .open > .dropdown-toggle.btn-bitbucket { 194 | background-image: none; 195 | } 196 | .btn-bitbucket.disabled, 197 | .btn-bitbucket[disabled], 198 | fieldset[disabled] .btn-bitbucket, 199 | .btn-bitbucket.disabled:hover, 200 | .btn-bitbucket[disabled]:hover, 201 | fieldset[disabled] .btn-bitbucket:hover, 202 | .btn-bitbucket.disabled:focus, 203 | .btn-bitbucket[disabled]:focus, 204 | fieldset[disabled] .btn-bitbucket:focus, 205 | .btn-bitbucket.disabled:active, 206 | .btn-bitbucket[disabled]:active, 207 | fieldset[disabled] .btn-bitbucket:active, 208 | .btn-bitbucket.disabled.active, 209 | .btn-bitbucket[disabled].active, 210 | fieldset[disabled] .btn-bitbucket.active { 211 | background-color: #205081; 212 | border-color: rgba(0, 0, 0, 0.2); 213 | } 214 | .btn-bitbucket .badge { 215 | color: #205081; 216 | background-color: #ffffff; 217 | } 218 | .btn-dropbox { 219 | color: #ffffff; 220 | background-color: #1087dd; 221 | border-color: rgba(0, 0, 0, 0.2); 222 | } 223 | .btn-dropbox:hover, 224 | .btn-dropbox:focus, 225 | .btn-dropbox:active, 226 | .btn-dropbox.active, 227 | .open > .dropdown-toggle.btn-dropbox { 228 | color: #ffffff; 229 | background-color: #0d6aad; 230 | border-color: rgba(0, 0, 0, 0.2); 231 | } 232 | .btn-dropbox:active, 233 | .btn-dropbox.active, 234 | .open > .dropdown-toggle.btn-dropbox { 235 | background-image: none; 236 | } 237 | .btn-dropbox.disabled, 238 | .btn-dropbox[disabled], 239 | fieldset[disabled] .btn-dropbox, 240 | .btn-dropbox.disabled:hover, 241 | .btn-dropbox[disabled]:hover, 242 | fieldset[disabled] .btn-dropbox:hover, 243 | .btn-dropbox.disabled:focus, 244 | .btn-dropbox[disabled]:focus, 245 | fieldset[disabled] .btn-dropbox:focus, 246 | .btn-dropbox.disabled:active, 247 | .btn-dropbox[disabled]:active, 248 | fieldset[disabled] .btn-dropbox:active, 249 | .btn-dropbox.disabled.active, 250 | .btn-dropbox[disabled].active, 251 | fieldset[disabled] .btn-dropbox.active { 252 | background-color: #1087dd; 253 | border-color: rgba(0, 0, 0, 0.2); 254 | } 255 | .btn-dropbox .badge { 256 | color: #1087dd; 257 | background-color: #ffffff; 258 | } 259 | .btn-facebook { 260 | color: #ffffff; 261 | background-color: #3b5998; 262 | border-color: rgba(0, 0, 0, 0.2); 263 | } 264 | .btn-facebook:hover, 265 | .btn-facebook:focus, 266 | .btn-facebook:active, 267 | .btn-facebook.active, 268 | .open > .dropdown-toggle.btn-facebook { 269 | color: #ffffff; 270 | background-color: #2d4373; 271 | border-color: rgba(0, 0, 0, 0.2); 272 | } 273 | .btn-facebook:active, 274 | .btn-facebook.active, 275 | .open > .dropdown-toggle.btn-facebook { 276 | background-image: none; 277 | } 278 | .btn-facebook.disabled, 279 | .btn-facebook[disabled], 280 | fieldset[disabled] .btn-facebook, 281 | .btn-facebook.disabled:hover, 282 | .btn-facebook[disabled]:hover, 283 | fieldset[disabled] .btn-facebook:hover, 284 | .btn-facebook.disabled:focus, 285 | .btn-facebook[disabled]:focus, 286 | fieldset[disabled] .btn-facebook:focus, 287 | .btn-facebook.disabled:active, 288 | .btn-facebook[disabled]:active, 289 | fieldset[disabled] .btn-facebook:active, 290 | .btn-facebook.disabled.active, 291 | .btn-facebook[disabled].active, 292 | fieldset[disabled] .btn-facebook.active { 293 | background-color: #3b5998; 294 | border-color: rgba(0, 0, 0, 0.2); 295 | } 296 | .btn-facebook .badge { 297 | color: #3b5998; 298 | background-color: #ffffff; 299 | } 300 | .btn-flickr { 301 | color: #ffffff; 302 | background-color: #ff0084; 303 | border-color: rgba(0, 0, 0, 0.2); 304 | } 305 | .btn-flickr:hover, 306 | .btn-flickr:focus, 307 | .btn-flickr:active, 308 | .btn-flickr.active, 309 | .open > .dropdown-toggle.btn-flickr { 310 | color: #ffffff; 311 | background-color: #cc006a; 312 | border-color: rgba(0, 0, 0, 0.2); 313 | } 314 | .btn-flickr:active, 315 | .btn-flickr.active, 316 | .open > .dropdown-toggle.btn-flickr { 317 | background-image: none; 318 | } 319 | .btn-flickr.disabled, 320 | .btn-flickr[disabled], 321 | fieldset[disabled] .btn-flickr, 322 | .btn-flickr.disabled:hover, 323 | .btn-flickr[disabled]:hover, 324 | fieldset[disabled] .btn-flickr:hover, 325 | .btn-flickr.disabled:focus, 326 | .btn-flickr[disabled]:focus, 327 | fieldset[disabled] .btn-flickr:focus, 328 | .btn-flickr.disabled:active, 329 | .btn-flickr[disabled]:active, 330 | fieldset[disabled] .btn-flickr:active, 331 | .btn-flickr.disabled.active, 332 | .btn-flickr[disabled].active, 333 | fieldset[disabled] .btn-flickr.active { 334 | background-color: #ff0084; 335 | border-color: rgba(0, 0, 0, 0.2); 336 | } 337 | .btn-flickr .badge { 338 | color: #ff0084; 339 | background-color: #ffffff; 340 | } 341 | .btn-foursquare { 342 | color: #ffffff; 343 | background-color: #0072b1; 344 | border-color: rgba(0, 0, 0, 0.2); 345 | } 346 | .btn-foursquare:hover, 347 | .btn-foursquare:focus, 348 | .btn-foursquare:active, 349 | .btn-foursquare.active, 350 | .open > .dropdown-toggle.btn-foursquare { 351 | color: #ffffff; 352 | background-color: #00517e; 353 | border-color: rgba(0, 0, 0, 0.2); 354 | } 355 | .btn-foursquare:active, 356 | .btn-foursquare.active, 357 | .open > .dropdown-toggle.btn-foursquare { 358 | background-image: none; 359 | } 360 | .btn-foursquare.disabled, 361 | .btn-foursquare[disabled], 362 | fieldset[disabled] .btn-foursquare, 363 | .btn-foursquare.disabled:hover, 364 | .btn-foursquare[disabled]:hover, 365 | fieldset[disabled] .btn-foursquare:hover, 366 | .btn-foursquare.disabled:focus, 367 | .btn-foursquare[disabled]:focus, 368 | fieldset[disabled] .btn-foursquare:focus, 369 | .btn-foursquare.disabled:active, 370 | .btn-foursquare[disabled]:active, 371 | fieldset[disabled] .btn-foursquare:active, 372 | .btn-foursquare.disabled.active, 373 | .btn-foursquare[disabled].active, 374 | fieldset[disabled] .btn-foursquare.active { 375 | background-color: #0072b1; 376 | border-color: rgba(0, 0, 0, 0.2); 377 | } 378 | .btn-foursquare .badge { 379 | color: #0072b1; 380 | background-color: #ffffff; 381 | } 382 | .btn-github { 383 | color: #ffffff; 384 | background-color: #444444; 385 | border-color: rgba(0, 0, 0, 0.2); 386 | } 387 | .btn-github:hover, 388 | .btn-github:focus, 389 | .btn-github:active, 390 | .btn-github.active, 391 | .open > .dropdown-toggle.btn-github { 392 | color: #ffffff; 393 | background-color: #2b2b2b; 394 | border-color: rgba(0, 0, 0, 0.2); 395 | } 396 | .btn-github:active, 397 | .btn-github.active, 398 | .open > .dropdown-toggle.btn-github { 399 | background-image: none; 400 | } 401 | .btn-github.disabled, 402 | .btn-github[disabled], 403 | fieldset[disabled] .btn-github, 404 | .btn-github.disabled:hover, 405 | .btn-github[disabled]:hover, 406 | fieldset[disabled] .btn-github:hover, 407 | .btn-github.disabled:focus, 408 | .btn-github[disabled]:focus, 409 | fieldset[disabled] .btn-github:focus, 410 | .btn-github.disabled:active, 411 | .btn-github[disabled]:active, 412 | fieldset[disabled] .btn-github:active, 413 | .btn-github.disabled.active, 414 | .btn-github[disabled].active, 415 | fieldset[disabled] .btn-github.active { 416 | background-color: #444444; 417 | border-color: rgba(0, 0, 0, 0.2); 418 | } 419 | .btn-github .badge { 420 | color: #444444; 421 | background-color: #ffffff; 422 | } 423 | .btn-google-plus { 424 | color: #ffffff; 425 | background-color: #dd4b39; 426 | border-color: rgba(0, 0, 0, 0.2); 427 | } 428 | .btn-google-plus:hover, 429 | .btn-google-plus:focus, 430 | .btn-google-plus:active, 431 | .btn-google-plus.active, 432 | .open > .dropdown-toggle.btn-google-plus { 433 | color: #ffffff; 434 | background-color: #c23321; 435 | border-color: rgba(0, 0, 0, 0.2); 436 | } 437 | .btn-google-plus:active, 438 | .btn-google-plus.active, 439 | .open > .dropdown-toggle.btn-google-plus { 440 | background-image: none; 441 | } 442 | .btn-google-plus.disabled, 443 | .btn-google-plus[disabled], 444 | fieldset[disabled] .btn-google-plus, 445 | .btn-google-plus.disabled:hover, 446 | .btn-google-plus[disabled]:hover, 447 | fieldset[disabled] .btn-google-plus:hover, 448 | .btn-google-plus.disabled:focus, 449 | .btn-google-plus[disabled]:focus, 450 | fieldset[disabled] .btn-google-plus:focus, 451 | .btn-google-plus.disabled:active, 452 | .btn-google-plus[disabled]:active, 453 | fieldset[disabled] .btn-google-plus:active, 454 | .btn-google-plus.disabled.active, 455 | .btn-google-plus[disabled].active, 456 | fieldset[disabled] .btn-google-plus.active { 457 | background-color: #dd4b39; 458 | border-color: rgba(0, 0, 0, 0.2); 459 | } 460 | .btn-google-plus .badge { 461 | color: #dd4b39; 462 | background-color: #ffffff; 463 | } 464 | .btn-instagram { 465 | color: #ffffff; 466 | background-color: #3f729b; 467 | border-color: rgba(0, 0, 0, 0.2); 468 | } 469 | .btn-instagram:hover, 470 | .btn-instagram:focus, 471 | .btn-instagram:active, 472 | .btn-instagram.active, 473 | .open > .dropdown-toggle.btn-instagram { 474 | color: #ffffff; 475 | background-color: #305777; 476 | border-color: rgba(0, 0, 0, 0.2); 477 | } 478 | .btn-instagram:active, 479 | .btn-instagram.active, 480 | .open > .dropdown-toggle.btn-instagram { 481 | background-image: none; 482 | } 483 | .btn-instagram.disabled, 484 | .btn-instagram[disabled], 485 | fieldset[disabled] .btn-instagram, 486 | .btn-instagram.disabled:hover, 487 | .btn-instagram[disabled]:hover, 488 | fieldset[disabled] .btn-instagram:hover, 489 | .btn-instagram.disabled:focus, 490 | .btn-instagram[disabled]:focus, 491 | fieldset[disabled] .btn-instagram:focus, 492 | .btn-instagram.disabled:active, 493 | .btn-instagram[disabled]:active, 494 | fieldset[disabled] .btn-instagram:active, 495 | .btn-instagram.disabled.active, 496 | .btn-instagram[disabled].active, 497 | fieldset[disabled] .btn-instagram.active { 498 | background-color: #3f729b; 499 | border-color: rgba(0, 0, 0, 0.2); 500 | } 501 | .btn-instagram .badge { 502 | color: #3f729b; 503 | background-color: #ffffff; 504 | } 505 | .btn-linkedin { 506 | color: #ffffff; 507 | background-color: #007bb6; 508 | border-color: rgba(0, 0, 0, 0.2); 509 | } 510 | .btn-linkedin:hover, 511 | .btn-linkedin:focus, 512 | .btn-linkedin:active, 513 | .btn-linkedin.active, 514 | .open > .dropdown-toggle.btn-linkedin { 515 | color: #ffffff; 516 | background-color: #005983; 517 | border-color: rgba(0, 0, 0, 0.2); 518 | } 519 | .btn-linkedin:active, 520 | .btn-linkedin.active, 521 | .open > .dropdown-toggle.btn-linkedin { 522 | background-image: none; 523 | } 524 | .btn-linkedin.disabled, 525 | .btn-linkedin[disabled], 526 | fieldset[disabled] .btn-linkedin, 527 | .btn-linkedin.disabled:hover, 528 | .btn-linkedin[disabled]:hover, 529 | fieldset[disabled] .btn-linkedin:hover, 530 | .btn-linkedin.disabled:focus, 531 | .btn-linkedin[disabled]:focus, 532 | fieldset[disabled] .btn-linkedin:focus, 533 | .btn-linkedin.disabled:active, 534 | .btn-linkedin[disabled]:active, 535 | fieldset[disabled] .btn-linkedin:active, 536 | .btn-linkedin.disabled.active, 537 | .btn-linkedin[disabled].active, 538 | fieldset[disabled] .btn-linkedin.active { 539 | background-color: #007bb6; 540 | border-color: rgba(0, 0, 0, 0.2); 541 | } 542 | .btn-linkedin .badge { 543 | color: #007bb6; 544 | background-color: #ffffff; 545 | } 546 | .btn-microsoft { 547 | color: #ffffff; 548 | background-color: #2672ec; 549 | border-color: rgba(0, 0, 0, 0.2); 550 | } 551 | .btn-microsoft:hover, 552 | .btn-microsoft:focus, 553 | .btn-microsoft:active, 554 | .btn-microsoft.active, 555 | .open > .dropdown-toggle.btn-microsoft { 556 | color: #ffffff; 557 | background-color: #125acd; 558 | border-color: rgba(0, 0, 0, 0.2); 559 | } 560 | .btn-microsoft:active, 561 | .btn-microsoft.active, 562 | .open > .dropdown-toggle.btn-microsoft { 563 | background-image: none; 564 | } 565 | .btn-microsoft.disabled, 566 | .btn-microsoft[disabled], 567 | fieldset[disabled] .btn-microsoft, 568 | .btn-microsoft.disabled:hover, 569 | .btn-microsoft[disabled]:hover, 570 | fieldset[disabled] .btn-microsoft:hover, 571 | .btn-microsoft.disabled:focus, 572 | .btn-microsoft[disabled]:focus, 573 | fieldset[disabled] .btn-microsoft:focus, 574 | .btn-microsoft.disabled:active, 575 | .btn-microsoft[disabled]:active, 576 | fieldset[disabled] .btn-microsoft:active, 577 | .btn-microsoft.disabled.active, 578 | .btn-microsoft[disabled].active, 579 | fieldset[disabled] .btn-microsoft.active { 580 | background-color: #2672ec; 581 | border-color: rgba(0, 0, 0, 0.2); 582 | } 583 | .btn-microsoft .badge { 584 | color: #2672ec; 585 | background-color: #ffffff; 586 | } 587 | .btn-openid { 588 | color: #ffffff; 589 | background-color: #f7931e; 590 | border-color: rgba(0, 0, 0, 0.2); 591 | } 592 | .btn-openid:hover, 593 | .btn-openid:focus, 594 | .btn-openid:active, 595 | .btn-openid.active, 596 | .open > .dropdown-toggle.btn-openid { 597 | color: #ffffff; 598 | background-color: #da7908; 599 | border-color: rgba(0, 0, 0, 0.2); 600 | } 601 | .btn-openid:active, 602 | .btn-openid.active, 603 | .open > .dropdown-toggle.btn-openid { 604 | background-image: none; 605 | } 606 | .btn-openid.disabled, 607 | .btn-openid[disabled], 608 | fieldset[disabled] .btn-openid, 609 | .btn-openid.disabled:hover, 610 | .btn-openid[disabled]:hover, 611 | fieldset[disabled] .btn-openid:hover, 612 | .btn-openid.disabled:focus, 613 | .btn-openid[disabled]:focus, 614 | fieldset[disabled] .btn-openid:focus, 615 | .btn-openid.disabled:active, 616 | .btn-openid[disabled]:active, 617 | fieldset[disabled] .btn-openid:active, 618 | .btn-openid.disabled.active, 619 | .btn-openid[disabled].active, 620 | fieldset[disabled] .btn-openid.active { 621 | background-color: #f7931e; 622 | border-color: rgba(0, 0, 0, 0.2); 623 | } 624 | .btn-openid .badge { 625 | color: #f7931e; 626 | background-color: #ffffff; 627 | } 628 | .btn-reddit { 629 | color: #000000; 630 | background-color: #eff7ff; 631 | border-color: rgba(0, 0, 0, 0.2); 632 | } 633 | .btn-reddit:hover, 634 | .btn-reddit:focus, 635 | .btn-reddit:active, 636 | .btn-reddit.active, 637 | .open > .dropdown-toggle.btn-reddit { 638 | color: #000000; 639 | background-color: #bcddff; 640 | border-color: rgba(0, 0, 0, 0.2); 641 | } 642 | .btn-reddit:active, 643 | .btn-reddit.active, 644 | .open > .dropdown-toggle.btn-reddit { 645 | background-image: none; 646 | } 647 | .btn-reddit.disabled, 648 | .btn-reddit[disabled], 649 | fieldset[disabled] .btn-reddit, 650 | .btn-reddit.disabled:hover, 651 | .btn-reddit[disabled]:hover, 652 | fieldset[disabled] .btn-reddit:hover, 653 | .btn-reddit.disabled:focus, 654 | .btn-reddit[disabled]:focus, 655 | fieldset[disabled] .btn-reddit:focus, 656 | .btn-reddit.disabled:active, 657 | .btn-reddit[disabled]:active, 658 | fieldset[disabled] .btn-reddit:active, 659 | .btn-reddit.disabled.active, 660 | .btn-reddit[disabled].active, 661 | fieldset[disabled] .btn-reddit.active { 662 | background-color: #eff7ff; 663 | border-color: rgba(0, 0, 0, 0.2); 664 | } 665 | .btn-reddit .badge { 666 | color: #eff7ff; 667 | background-color: #000000; 668 | } 669 | .btn-soundcloud { 670 | color: #ffffff; 671 | background-color: #ff5500; 672 | border-color: rgba(0, 0, 0, 0.2); 673 | } 674 | .btn-soundcloud:hover, 675 | .btn-soundcloud:focus, 676 | .btn-soundcloud:active, 677 | .btn-soundcloud.active, 678 | .open > .dropdown-toggle.btn-soundcloud { 679 | color: #ffffff; 680 | background-color: #cc4400; 681 | border-color: rgba(0, 0, 0, 0.2); 682 | } 683 | .btn-soundcloud:active, 684 | .btn-soundcloud.active, 685 | .open > .dropdown-toggle.btn-soundcloud { 686 | background-image: none; 687 | } 688 | .btn-soundcloud.disabled, 689 | .btn-soundcloud[disabled], 690 | fieldset[disabled] .btn-soundcloud, 691 | .btn-soundcloud.disabled:hover, 692 | .btn-soundcloud[disabled]:hover, 693 | fieldset[disabled] .btn-soundcloud:hover, 694 | .btn-soundcloud.disabled:focus, 695 | .btn-soundcloud[disabled]:focus, 696 | fieldset[disabled] .btn-soundcloud:focus, 697 | .btn-soundcloud.disabled:active, 698 | .btn-soundcloud[disabled]:active, 699 | fieldset[disabled] .btn-soundcloud:active, 700 | .btn-soundcloud.disabled.active, 701 | .btn-soundcloud[disabled].active, 702 | fieldset[disabled] .btn-soundcloud.active { 703 | background-color: #ff5500; 704 | border-color: rgba(0, 0, 0, 0.2); 705 | } 706 | .btn-soundcloud .badge { 707 | color: #ff5500; 708 | background-color: #ffffff; 709 | } 710 | .btn-tumblr { 711 | color: #ffffff; 712 | background-color: #2c4762; 713 | border-color: rgba(0, 0, 0, 0.2); 714 | } 715 | .btn-tumblr:hover, 716 | .btn-tumblr:focus, 717 | .btn-tumblr:active, 718 | .btn-tumblr.active, 719 | .open > .dropdown-toggle.btn-tumblr { 720 | color: #ffffff; 721 | background-color: #1c2d3f; 722 | border-color: rgba(0, 0, 0, 0.2); 723 | } 724 | .btn-tumblr:active, 725 | .btn-tumblr.active, 726 | .open > .dropdown-toggle.btn-tumblr { 727 | background-image: none; 728 | } 729 | .btn-tumblr.disabled, 730 | .btn-tumblr[disabled], 731 | fieldset[disabled] .btn-tumblr, 732 | .btn-tumblr.disabled:hover, 733 | .btn-tumblr[disabled]:hover, 734 | fieldset[disabled] .btn-tumblr:hover, 735 | .btn-tumblr.disabled:focus, 736 | .btn-tumblr[disabled]:focus, 737 | fieldset[disabled] .btn-tumblr:focus, 738 | .btn-tumblr.disabled:active, 739 | .btn-tumblr[disabled]:active, 740 | fieldset[disabled] .btn-tumblr:active, 741 | .btn-tumblr.disabled.active, 742 | .btn-tumblr[disabled].active, 743 | fieldset[disabled] .btn-tumblr.active { 744 | background-color: #2c4762; 745 | border-color: rgba(0, 0, 0, 0.2); 746 | } 747 | .btn-tumblr .badge { 748 | color: #2c4762; 749 | background-color: #ffffff; 750 | } 751 | .btn-twitter { 752 | color: #ffffff; 753 | background-color: #55acee; 754 | border-color: rgba(0, 0, 0, 0.2); 755 | } 756 | .btn-twitter:hover, 757 | .btn-twitter:focus, 758 | .btn-twitter:active, 759 | .btn-twitter.active, 760 | .open > .dropdown-toggle.btn-twitter { 761 | color: #ffffff; 762 | background-color: #2795e9; 763 | border-color: rgba(0, 0, 0, 0.2); 764 | } 765 | .btn-twitter:active, 766 | .btn-twitter.active, 767 | .open > .dropdown-toggle.btn-twitter { 768 | background-image: none; 769 | } 770 | .btn-twitter.disabled, 771 | .btn-twitter[disabled], 772 | fieldset[disabled] .btn-twitter, 773 | .btn-twitter.disabled:hover, 774 | .btn-twitter[disabled]:hover, 775 | fieldset[disabled] .btn-twitter:hover, 776 | .btn-twitter.disabled:focus, 777 | .btn-twitter[disabled]:focus, 778 | fieldset[disabled] .btn-twitter:focus, 779 | .btn-twitter.disabled:active, 780 | .btn-twitter[disabled]:active, 781 | fieldset[disabled] .btn-twitter:active, 782 | .btn-twitter.disabled.active, 783 | .btn-twitter[disabled].active, 784 | fieldset[disabled] .btn-twitter.active { 785 | background-color: #55acee; 786 | border-color: rgba(0, 0, 0, 0.2); 787 | } 788 | .btn-twitter .badge { 789 | color: #55acee; 790 | background-color: #ffffff; 791 | } 792 | .btn-vimeo { 793 | color: #ffffff; 794 | background-color: #1ab7ea; 795 | border-color: rgba(0, 0, 0, 0.2); 796 | } 797 | .btn-vimeo:hover, 798 | .btn-vimeo:focus, 799 | .btn-vimeo:active, 800 | .btn-vimeo.active, 801 | .open > .dropdown-toggle.btn-vimeo { 802 | color: #ffffff; 803 | background-color: #1295bf; 804 | border-color: rgba(0, 0, 0, 0.2); 805 | } 806 | .btn-vimeo:active, 807 | .btn-vimeo.active, 808 | .open > .dropdown-toggle.btn-vimeo { 809 | background-image: none; 810 | } 811 | .btn-vimeo.disabled, 812 | .btn-vimeo[disabled], 813 | fieldset[disabled] .btn-vimeo, 814 | .btn-vimeo.disabled:hover, 815 | .btn-vimeo[disabled]:hover, 816 | fieldset[disabled] .btn-vimeo:hover, 817 | .btn-vimeo.disabled:focus, 818 | .btn-vimeo[disabled]:focus, 819 | fieldset[disabled] .btn-vimeo:focus, 820 | .btn-vimeo.disabled:active, 821 | .btn-vimeo[disabled]:active, 822 | fieldset[disabled] .btn-vimeo:active, 823 | .btn-vimeo.disabled.active, 824 | .btn-vimeo[disabled].active, 825 | fieldset[disabled] .btn-vimeo.active { 826 | background-color: #1ab7ea; 827 | border-color: rgba(0, 0, 0, 0.2); 828 | } 829 | .btn-vimeo .badge { 830 | color: #1ab7ea; 831 | background-color: #ffffff; 832 | } 833 | .btn-vk { 834 | color: #ffffff; 835 | background-color: #587ea3; 836 | border-color: rgba(0, 0, 0, 0.2); 837 | } 838 | .btn-vk:hover, 839 | .btn-vk:focus, 840 | .btn-vk:active, 841 | .btn-vk.active, 842 | .open > .dropdown-toggle.btn-vk { 843 | color: #ffffff; 844 | background-color: #466482; 845 | border-color: rgba(0, 0, 0, 0.2); 846 | } 847 | .btn-vk:active, 848 | .btn-vk.active, 849 | .open > .dropdown-toggle.btn-vk { 850 | background-image: none; 851 | } 852 | .btn-vk.disabled, 853 | .btn-vk[disabled], 854 | fieldset[disabled] .btn-vk, 855 | .btn-vk.disabled:hover, 856 | .btn-vk[disabled]:hover, 857 | fieldset[disabled] .btn-vk:hover, 858 | .btn-vk.disabled:focus, 859 | .btn-vk[disabled]:focus, 860 | fieldset[disabled] .btn-vk:focus, 861 | .btn-vk.disabled:active, 862 | .btn-vk[disabled]:active, 863 | fieldset[disabled] .btn-vk:active, 864 | .btn-vk.disabled.active, 865 | .btn-vk[disabled].active, 866 | fieldset[disabled] .btn-vk.active { 867 | background-color: #587ea3; 868 | border-color: rgba(0, 0, 0, 0.2); 869 | } 870 | .btn-vk .badge { 871 | color: #587ea3; 872 | background-color: #ffffff; 873 | } 874 | .btn-yahoo { 875 | color: #ffffff; 876 | background-color: #720e9e; 877 | border-color: rgba(0, 0, 0, 0.2); 878 | } 879 | .btn-yahoo:hover, 880 | .btn-yahoo:focus, 881 | .btn-yahoo:active, 882 | .btn-yahoo.active, 883 | .open > .dropdown-toggle.btn-yahoo { 884 | color: #ffffff; 885 | background-color: #500a6f; 886 | border-color: rgba(0, 0, 0, 0.2); 887 | } 888 | .btn-yahoo:active, 889 | .btn-yahoo.active, 890 | .open > .dropdown-toggle.btn-yahoo { 891 | background-image: none; 892 | } 893 | .btn-yahoo.disabled, 894 | .btn-yahoo[disabled], 895 | fieldset[disabled] .btn-yahoo, 896 | .btn-yahoo.disabled:hover, 897 | .btn-yahoo[disabled]:hover, 898 | fieldset[disabled] .btn-yahoo:hover, 899 | .btn-yahoo.disabled:focus, 900 | .btn-yahoo[disabled]:focus, 901 | fieldset[disabled] .btn-yahoo:focus, 902 | .btn-yahoo.disabled:active, 903 | .btn-yahoo[disabled]:active, 904 | fieldset[disabled] .btn-yahoo:active, 905 | .btn-yahoo.disabled.active, 906 | .btn-yahoo[disabled].active, 907 | fieldset[disabled] .btn-yahoo.active { 908 | background-color: #720e9e; 909 | border-color: rgba(0, 0, 0, 0.2); 910 | } 911 | .btn-yahoo .badge { 912 | color: #720e9e; 913 | background-color: #ffffff; 914 | } 915 | -------------------------------------------------------------------------------- /public/css/socialbuttons.less: -------------------------------------------------------------------------------- 1 | /* 2 | * Social Buttons for Bootstrap 3 | * 4 | * Copyright 2013-2014 Panayiotis Lipiridis 5 | * Licensed under the MIT License 6 | * 7 | * https://github.com/lipis/bootstrap-social 8 | */ 9 | 10 | // Update this to point to Bootstrap's mixins file. 11 | @import "../../../../../vendor/twbs/bootstrap/less/mixins.less"; 12 | 13 | @font-size-base: 14px; 14 | @font-size-large: ceil((@font-size-base * 1.25)); // ~18px 15 | @font-size-small: ceil((@font-size-base * 0.85)); // ~12px 16 | 17 | @line-height-base: 1.428571429; // 20/14 18 | @line-height-computed: floor((@font-size-base * @line-height-base)); // ~20px 19 | 20 | @padding-base-vertical: 10px; 21 | @padding-base-horizontal: 15px; 22 | 23 | @padding-large-vertical: 10px; 24 | @padding-large-horizontal: 16px; 25 | 26 | @padding-small-vertical: 5px; 27 | @padding-small-horizontal: 10px; 28 | 29 | @line-height-large: 1.33; 30 | @line-height-small: 1.5; 31 | 32 | @bs-height-base: (@line-height-computed + @padding-base-vertical * 2); 33 | @bs-height-lg: (floor(@font-size-large * @line-height-base) + @padding-large-vertical * 2); 34 | @bs-height-sm: (floor(@font-size-small * 1.5) + @padding-small-vertical * 2); 35 | @bs-height-xs: (floor(@font-size-small * 1.2) + @padding-small-vertical + 1); 36 | 37 | .btn-social { 38 | position: relative; 39 | padding-left: (@bs-height-base + @padding-base-horizontal); 40 | padding-top: @padding-base-vertical; 41 | padding-bottom: @padding-base-vertical; 42 | text-align: left; 43 | font-weight: bold; 44 | white-space: nowrap; 45 | overflow: hidden; 46 | text-overflow: ellipsis; 47 | :first-child { 48 | position: absolute; 49 | left: 0; 50 | top: 0; 51 | bottom: 0; 52 | width: @bs-height-base; 53 | line-height: (@bs-height-base + 2); 54 | font-size: 1.6em; 55 | text-align: center; 56 | border-right: 1px solid rgba(0, 0, 0, 0.15); 57 | } 58 | &.btn-lg { 59 | padding-left: (@bs-height-lg + @padding-large-horizontal); 60 | padding-top: @padding-large-vertical; 61 | padding-bottom: @padding-large-vertical; 62 | :first-child { 63 | line-height: @bs-height-lg; 64 | width: @bs-height-lg; 65 | font-size: 1.8em; 66 | } 67 | } 68 | &.btn-sm { 69 | padding-left: (@bs-height-sm + @padding-small-horizontal); 70 | padding-top: @padding-small-vertical; 71 | padding-bottom: @padding-small-vertical; 72 | :first-child { 73 | line-height: @bs-height-sm; 74 | width: @bs-height-sm; 75 | font-size: 1.4em; 76 | } 77 | } 78 | &.btn-xs { 79 | padding-left: (@bs-height-xs + @padding-small-horizontal); 80 | :first-child { 81 | line-height: @bs-height-xs; 82 | width: @bs-height-xs; 83 | font-size: 1.2em; 84 | } 85 | } 86 | } 87 | 88 | .btn-social-icon { 89 | .btn-social; 90 | height: (@bs-height-base + 2); 91 | width: (@bs-height-base + 2); 92 | padding: 0; 93 | :first-child { 94 | border: none; 95 | text-align: center; 96 | width: 100%!important; 97 | } 98 | &.btn-lg { 99 | height: @bs-height-lg; 100 | width: @bs-height-lg; 101 | padding-left: 0; 102 | padding-right: 0; 103 | } 104 | &.btn-sm { 105 | height: (@bs-height-sm + 2); 106 | width: (@bs-height-sm + 2); 107 | padding-left: 0; 108 | padding-right: 0; 109 | } 110 | &.btn-xs { 111 | height: (@bs-height-xs + 2); 112 | width: (@bs-height-xs + 2); 113 | padding-left: 0; 114 | padding-right: 0; 115 | } 116 | } 117 | 118 | .btn-social(@color-bg, @color: #fff) { 119 | background-color: @color-bg; 120 | .button-variant(@color, @color-bg, rgba(0,0,0,.2)); 121 | } 122 | 123 | 124 | .btn-adn { .btn-social(#d87a68); } 125 | .btn-bitbucket { .btn-social(#205081); } 126 | .btn-dropbox { .btn-social(#1087dd); } 127 | .btn-facebook { .btn-social(#3b5998); } 128 | .btn-flickr { .btn-social(#ff0084); } 129 | .btn-foursquare { .btn-social(#0072b1); } 130 | .btn-github { .btn-social(#444444); } 131 | .btn-google-plus { .btn-social(#dd4b39); } 132 | .btn-instagram { .btn-social(#3f729b); } 133 | .btn-linkedin { .btn-social(#007bb6); } 134 | .btn-microsoft { .btn-social(#2672ec); } 135 | .btn-openid { .btn-social(#f7931e); } 136 | .btn-reddit { .btn-social(#eff7ff, #000); } 137 | .btn-soundcloud { .btn-social(#ff5500); } 138 | .btn-tumblr { .btn-social(#2c4762); } 139 | .btn-twitter { .btn-social(#55acee); } 140 | .btn-vimeo { .btn-social(#1ab7ea); } 141 | .btn-vk { .btn-social(#587ea3); } 142 | .btn-yahoo { .btn-social(#720e9e); } 143 | -------------------------------------------------------------------------------- /src/Mmanos/Social/Facades/Social.php: -------------------------------------------------------------------------------- 1 | factory = new ServiceFactory; 34 | $this->storage = new Session; 35 | } 36 | 37 | /** 38 | * Return an instance of the requested service. 39 | * 40 | * @param string $provider 41 | * @param string $url 42 | * @param array $scope 43 | * 44 | * @return \OAuth\Common\Service\AbstractService 45 | * @throws \Exception 46 | */ 47 | public function service($provider, $url = null, $scope = null) 48 | { 49 | $info = Config::get('laravel-social::providers.' . strtolower($provider)); 50 | 51 | if (empty($info) || !is_array($info)) { 52 | throw new Exception('Missing configuration details for Social service: ' . $provider); 53 | } 54 | 55 | $client_id = array_get($info, 'client_id'); 56 | $client_secret = array_get($info, 'client_secret'); 57 | $scope = is_null($scope) ? array_get($info, 'scope') : $scope; 58 | 59 | if (empty($client_id) || empty($client_secret)) { 60 | throw new Exception('Missing client id/secret for Social service: ' . $provider); 61 | } 62 | 63 | return $this->factory->createService( 64 | ucfirst($provider), 65 | new Credentials($client_id, $client_secret, $url ?: URL::current()), 66 | $this->storage, 67 | $scope 68 | ); 69 | } 70 | 71 | /** 72 | * Return the OAuth spec used by the given service provider. 73 | * 74 | * @param string $provider 75 | * 76 | * @return integer 77 | */ 78 | public function oauthSpec($provider) 79 | { 80 | $service = $this->service($provider); 81 | 82 | if (false !== stristr(get_class($service), 'OAuth1')) { 83 | return 1; 84 | } 85 | else if (false !== stristr(get_class($service), 'OAuth2')) { 86 | return 2; 87 | } 88 | 89 | return null; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/Mmanos/Social/SocialServiceProvider.php: -------------------------------------------------------------------------------- 1 | package('mmanos/laravel-social'); 24 | 25 | if ($route = Config::get('laravel-social::route')) { 26 | Route::get($route . '/login/{provider}', array( 27 | 'as' => 'social-login', 28 | 'uses' => 'Mmanos\Social\SocialController@getLogin', 29 | )); 30 | Route::get($route . '/connect/{provider}', array( 31 | 'as' => 'social-connect', 32 | 'uses' => 'Mmanos\Social\SocialController@getConnect', 33 | )); 34 | Route::controller($route, 'Mmanos\Social\SocialController'); 35 | } 36 | } 37 | 38 | /** 39 | * Register the service provider. 40 | * 41 | * @return void 42 | */ 43 | public function register() 44 | { 45 | $this->app->bindShared('social', function ($app) { 46 | return new \Mmanos\Social\Social; 47 | }); 48 | } 49 | 50 | /** 51 | * Get the services provided by the provider. 52 | * 53 | * @return array 54 | */ 55 | public function provides() 56 | { 57 | return array(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Mmanos/Social/SocialTrait.php: -------------------------------------------------------------------------------- 1 | hasMany('Mmanos\Social\Provider'); 13 | } 14 | 15 | /** 16 | * Return the reqeusted social provider associated with this user. 17 | * 18 | * @param string $name 19 | * 20 | * @var Provider 21 | */ 22 | public function provider($name) 23 | { 24 | $providers = $this->providers->filter(function ($provider) use ($name) { 25 | return strtolower($provider->provider) == strtolower($name); 26 | }); 27 | 28 | return $providers->first(); 29 | } 30 | 31 | /** 32 | * Return true if this user has connected to the requested social provider. 33 | * False, otherwise. 34 | * 35 | * @param string $name 36 | * 37 | * @var boolean 38 | */ 39 | public function hasProvider($name) 40 | { 41 | return $this->provider($name) ? true : false; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/config/config.php: -------------------------------------------------------------------------------- 1 | 'auth/social', 16 | 17 | /* 18 | |-------------------------------------------------------------------------- 19 | | Providers Table 20 | |-------------------------------------------------------------------------- 21 | | 22 | | Specify the name of the database table to use for linked providers. 23 | | 24 | */ 25 | 26 | 'table' => 'user_providers', 27 | 28 | /* 29 | |-------------------------------------------------------------------------- 30 | | Providers 31 | |-------------------------------------------------------------------------- 32 | | 33 | | Specify the oauth service provider information for each service provider 34 | | you want enabled, to be used by the PHPoAuthLib package. 35 | | 36 | */ 37 | 38 | 'providers' => array( 39 | 40 | 'facebook' => array( 41 | 'client_id' => '', 42 | 'client_secret' => '', 43 | 'scope' => array('email'), 44 | 'fetch_user_info' => function ($service) { 45 | $result = json_decode($service->request('/me'), true); 46 | return array( 47 | 'id' => array_get($result, 'id'), 48 | 'email' => array_get($result, 'email'), 49 | 'first_name' => array_get($result, 'first_name'), 50 | 'last_name' => array_get($result, 'last_name') 51 | ); 52 | }, 53 | ), 54 | 55 | 'twitter' => array( 56 | 'client_id' => '', 57 | 'client_secret' => '', 58 | 'scope' => array(), 59 | 'fetch_user_info' => function ($service) { 60 | $result = json_decode($service->request('account/verify_credentials.json'), true); 61 | return array( 62 | 'id' => array_get($result, 'id'), 63 | 'email' => null, 64 | 'first_name' => array_get(explode(' ', array_get($result, 'name')), 0), 65 | 'last_name' => array_get(explode(' ', array_get($result, 'name')), 1) 66 | ); 67 | }, 68 | ), 69 | 70 | 'google' => array( 71 | 'client_id' => '', 72 | 'client_secret' => '', 73 | 'scope' => array('userinfo_email', 'userinfo_profile'), 74 | 'offline' => false, 75 | 'fetch_user_info' => function ($service) { 76 | $result = json_decode($service->request('https://www.googleapis.com/oauth2/v1/userinfo'), true); 77 | return array( 78 | 'id' => array_get($result, 'id'), 79 | 'email' => array_get($result, 'email'), 80 | 'first_name' => array_get(explode(' ', array_get($result, 'name')), 0), 81 | 'last_name' => array_get(explode(' ', array_get($result, 'name')), 1) 82 | ); 83 | }, 84 | ), 85 | 86 | ), 87 | 88 | /* 89 | |-------------------------------------------------------------------------- 90 | | New User Validation 91 | |-------------------------------------------------------------------------- 92 | | 93 | | Define the validation rules to apply against new user data. 94 | | This may be an array of validation rules or a Closure which returns a 95 | | Validator instance. 96 | | 97 | */ 98 | 99 | 'user_validation' => array( 100 | 'email' => 'required|email', 101 | 'first_name' => 'required', 102 | ), 103 | 104 | /* 105 | |-------------------------------------------------------------------------- 106 | | Create User Callback 107 | |-------------------------------------------------------------------------- 108 | | 109 | | Use the given data to create and return a new user's id. 110 | | 111 | */ 112 | 113 | 'create_user' => function ($data) { 114 | $user = new User; 115 | $user->email = array_get($data, 'email'); 116 | $user->password = Hash::make(Str::random()); 117 | $user->first_name = array_get($data, 'first_name'); 118 | $user->save(); 119 | 120 | return $user->id; 121 | }, 122 | 123 | /* 124 | |-------------------------------------------------------------------------- 125 | | New User Failed Validation Redirect 126 | |-------------------------------------------------------------------------- 127 | | 128 | | Define the action to redirect to if/when a new user fails the validation 129 | | rules. Defaults to a built-in "complete" action. Override to further 130 | | customize how this flow is handled. 131 | | 132 | */ 133 | 134 | 'user_failed_validation_redirect' => 'Mmanos\Social\SocialController@getComplete', 135 | 136 | /* 137 | |-------------------------------------------------------------------------- 138 | | Error Flash Variable Name 139 | |-------------------------------------------------------------------------- 140 | | 141 | | Define the variable name to use when flashing an error to session before 142 | | redirecting after an error is encountered. 143 | | 144 | */ 145 | 146 | 'error_flash_var' => 'error', 147 | 148 | /* 149 | |-------------------------------------------------------------------------- 150 | | Success Flash Variable Name 151 | |-------------------------------------------------------------------------- 152 | | 153 | | Define the variable name to use when flashing a success to session before 154 | | redirecting after a social provider account was successfully connected. 155 | | 156 | */ 157 | 158 | 'success_flash_var' => 'success', 159 | 160 | ); 161 | -------------------------------------------------------------------------------- /src/controllers/SocialController.php: -------------------------------------------------------------------------------- 1 | with( 60 | Config::get('laravel-social::error_flash_var'), 61 | 'There was a problem logging in to your account (1).' 62 | ); 63 | } 64 | 65 | $provider = ucfirst($provider); 66 | 67 | try { 68 | $service = Social::service($provider); 69 | 70 | if (Config::get('laravel-social::providers.' . strtolower($provider) . '.offline')) { 71 | $service->setAccessType('offline'); 72 | } 73 | } catch (Exception $e) { 74 | App::abort(404); 75 | } 76 | 77 | if (2 === Social::oauthSpec($provider)) { 78 | return $this->oauth2Login($provider, $service); 79 | } 80 | else { 81 | return $this->oauth1Login($provider, $service); 82 | } 83 | } 84 | 85 | /** 86 | * Login to an OAuth2 service. 87 | * 88 | * @param string $provider 89 | * @param \OAuth\Common\Service\AbstractService $service 90 | * 91 | * @return Redirect 92 | */ 93 | protected function oauth2Login($provider, $service) 94 | { 95 | if ($code = Input::get('code')) { 96 | try { 97 | $token = $service->requestAccessToken($code); 98 | } catch (Exception $e) { 99 | return Redirect::to(Session::pull('mmanos.social.onerror', '/')) 100 | ->with( 101 | Config::get('laravel-social::error_flash_var'), 102 | 'There was a problem logging in to your account (2).' 103 | ); 104 | } 105 | 106 | return $this->processLogin($provider, $service, array( 107 | 'token' => $token->getAccessToken(), 108 | 'refresh_token' => $token->getRefreshToken(), 109 | 'end_of_life' => $token->getEndOfLife(), 110 | 'extra_params' => $token->getExtraParams(), 111 | )); 112 | } 113 | 114 | return Redirect::to((string) $service->getAuthorizationUri()); 115 | } 116 | 117 | /** 118 | * Login to an OAuth1 consumer. 119 | * 120 | * @param string $provider 121 | * @param \OAuth\Common\Service\AbstractService $service 122 | * 123 | * @return Redirect 124 | */ 125 | protected function oauth1Login($provider, $service) 126 | { 127 | if ($oauth_token = Input::get('oauth_token')) { 128 | try { 129 | $token = $service->requestAccessToken( 130 | $oauth_token, 131 | Input::get('oauth_verifier'), 132 | $service->getStorage()->retrieveAccessToken($provider)->getRequestTokenSecret() 133 | ); 134 | } catch (Exception $e) { 135 | return Redirect::to(Session::pull('mmanos.social.onerror', '/')) 136 | ->with( 137 | Config::get('laravel-social::error_flash_var'), 138 | 'There was a problem logging in to your account (3).' 139 | ); 140 | } 141 | 142 | return $this->processLogin($provider, $service, array( 143 | 'token' => $token->getAccessToken(), 144 | 'secret' => $token->getAccessTokenSecret(), 145 | 'refresh_token' => $token->getRefreshToken(), 146 | 'end_of_life' => $token->getEndOfLife(), 147 | 'extra_params' => $token->getExtraParams(), 148 | )); 149 | } 150 | 151 | try { 152 | // Extra request needed for oauth1 to get a request token. 153 | $token = $service->requestRequestToken(); 154 | } catch (Exception $e) { 155 | return Redirect::to(Session::pull('mmanos.social.onerror', '/')) 156 | ->with( 157 | Config::get('laravel-social::error_flash_var'), 158 | 'There was a problem logging in to your account (4).' 159 | ); 160 | } 161 | 162 | return Redirect::to((string) $service->getAuthorizationUri(array( 163 | 'oauth_token' => $token->getRequestToken(), 164 | ))); 165 | } 166 | 167 | /** 168 | * Process the response from a provider login attempt. 169 | * 170 | * @param string $provider 171 | * @param \OAuth\Common\Service\AbstractService $service 172 | * @param array $access_token 173 | * 174 | * @return Redirect 175 | */ 176 | protected function processLogin($provider, $service, $access_token) 177 | { 178 | $user_info_callback = Config::get( 179 | 'laravel-social::providers.' . strtolower($provider) . '.fetch_user_info' 180 | ); 181 | 182 | if (empty($user_info_callback) || !$user_info_callback instanceof Closure) { 183 | return Redirect::to(Session::pull('mmanos.social.onerror', '/')) 184 | ->with( 185 | Config::get('laravel-social::error_flash_var'), 186 | 'There was a problem logging in to your account (5).' 187 | ); 188 | } 189 | 190 | try { 191 | $user_info = $user_info_callback($service); 192 | } catch (Exception $e) {} 193 | 194 | if (empty($user_info) || !is_array($user_info)) { 195 | return Redirect::to(Session::pull('mmanos.social.onerror', '/')) 196 | ->with( 197 | Config::get('laravel-social::error_flash_var'), 198 | 'There was a problem logging in to your account (6).' 199 | ); 200 | } 201 | 202 | if (empty($user_info['id'])) { 203 | return Redirect::to(Session::pull('mmanos.social.onerror', '/')) 204 | ->with( 205 | Config::get('laravel-social::error_flash_var'), 206 | 'There was a problem logging in to your account (7).' 207 | ); 208 | } 209 | 210 | $provider_id = array_get($user_info, 'id'); 211 | 212 | $user_provider = Provider::where('provider', strtolower($provider)) 213 | ->where('provider_id', $provider_id) 214 | ->first(); 215 | 216 | if ($user_provider) { 217 | Auth::loginUsingId($user_provider->user_id); 218 | return Redirect::to(Session::pull('mmanos.social.onsuccess', '/')); 219 | } 220 | 221 | if ($user_validation = Config::get('laravel-social::user_validation')) { 222 | if ($user_validation instanceof Closure) { 223 | $validator = $user_validation($user_info); 224 | } 225 | else { 226 | $validator = Validator::make($user_info, (array) $user_validation); 227 | } 228 | 229 | if ($validator && $validator->fails()) { 230 | Session::put('mmanos.social.pending', array( 231 | 'provider' => $provider, 232 | 'provider_id' => $provider_id, 233 | 'user_info' => $user_info, 234 | 'access_token' => $access_token, 235 | )); 236 | Session::put('mmanos.social.failed_fields', array_keys($validator->failed())); 237 | 238 | return Redirect::action(Config::get('laravel-social::user_failed_validation_redirect')) 239 | ->withErrors($validator); 240 | } 241 | } 242 | 243 | $create_user_callback = Config::get('laravel-social::create_user'); 244 | if (empty($create_user_callback) || !$create_user_callback instanceof Closure) { 245 | return Redirect::to(Session::pull('mmanos.social.onerror', '/')) 246 | ->with( 247 | Config::get('laravel-social::error_flash_var'), 248 | 'There was a problem logging in to your account (8).' 249 | ); 250 | } 251 | 252 | $user_id = $create_user_callback($user_info); 253 | if (!$user_id || !is_numeric($user_id) || $user_id <= 0) { 254 | return Redirect::to(Session::pull('mmanos.social.onerror', '/')) 255 | ->with( 256 | Config::get('laravel-social::error_flash_var'), 257 | 'There was a problem logging in to your account (9).' 258 | ); 259 | } 260 | 261 | $this->linkProvider($user_id, $provider, $provider_id, $access_token); 262 | 263 | Auth::loginUsingId($user_id); 264 | return Redirect::to(Session::pull('mmanos.social.onsuccess', '/')); 265 | } 266 | 267 | /** 268 | * Complete login action. 269 | * 270 | * @return View 271 | */ 272 | public function getComplete() 273 | { 274 | $user_data = Session::get('mmanos.social.pending'); 275 | if (empty($user_data) || !is_array($user_data)) { 276 | return Redirect::to(Session::pull('mmanos.social.onerror', '/')) 277 | ->with( 278 | Config::get('laravel-social::error_flash_var'), 279 | 'There was a problem logging in to your account (10).' 280 | ); 281 | } 282 | 283 | $failed_fields = Session::get('mmanos.social.failed_fields'); 284 | if (empty($failed_fields) || !is_array($failed_fields)) { 285 | return Redirect::to(Session::pull('mmanos.social.onerror', '/')) 286 | ->with( 287 | Config::get('laravel-social::error_flash_var'), 288 | 'There was a problem logging in to your account (11).' 289 | ); 290 | } 291 | 292 | return View::make('laravel-social::social.complete', array( 293 | 'failed_fields' => $failed_fields, 294 | 'info' => array_get($user_data, 'user_info'), 295 | )); 296 | } 297 | 298 | /** 299 | * Handle the complete login form submission. 300 | * 301 | * @return Redirect 302 | */ 303 | public function postComplete() 304 | { 305 | $user_data = Session::get('mmanos.social.pending'); 306 | if (empty($user_data) || !is_array($user_data)) { 307 | return Redirect::to(Session::pull('mmanos.social.onerror', '/')) 308 | ->with( 309 | Config::get('laravel-social::error_flash_var'), 310 | 'There was a problem logging in to your account (12).' 311 | ); 312 | } 313 | 314 | $user_info = array_merge(array_get($user_data, 'user_info'), Input::all()); 315 | 316 | $user_validation = Config::get('laravel-social::user_validation'); 317 | if ($user_validation instanceof Closure) { 318 | $validator = $user_validation($user_info); 319 | } 320 | else { 321 | $validator = Validator::make($user_info, (array) $user_validation); 322 | } 323 | 324 | if ($validator->fails()) { 325 | return Redirect::action('Mmanos\Social\SocialController@getComplete') 326 | ->withInput() 327 | ->withErrors($validator); 328 | } 329 | 330 | $create_user_callback = Config::get('laravel-social::create_user'); 331 | if (empty($create_user_callback) || !$create_user_callback instanceof Closure) { 332 | return Redirect::to(Session::pull('mmanos.social.onerror', '/')) 333 | ->with( 334 | Config::get('laravel-social::error_flash_var'), 335 | 'There was a problem logging in to your account (13).' 336 | ); 337 | } 338 | 339 | $user_id = $create_user_callback($user_info); 340 | if (!$user_id || !is_numeric($user_id) || $user_id <= 0) { 341 | return Redirect::to(Session::pull('mmanos.social.onerror', '/')) 342 | ->with( 343 | Config::get('laravel-social::error_flash_var'), 344 | 'There was a problem logging in to your account (14).' 345 | ); 346 | } 347 | 348 | $provider = array_get($user_data, 'provider'); 349 | $provider_id = array_get($user_data, 'provider_id'); 350 | $access_token = array_get($user_data, 'access_token'); 351 | 352 | $this->linkProvider($user_id, $provider, $provider_id, $access_token); 353 | 354 | Session::forget('mmanos.social.pending'); 355 | Session::forget('mmanos.social.failed_fields'); 356 | 357 | Auth::loginUsingId($user_id); 358 | return Redirect::to(Session::pull('mmanos.social.onsuccess', '/')); 359 | } 360 | 361 | /** 362 | * Connect action. 363 | * 364 | * @param string $provider 365 | * 366 | * @return mixed 367 | */ 368 | public function getConnect($provider = null) 369 | { 370 | if (empty($provider)) { 371 | App::abort(404); 372 | } 373 | 374 | $referer = Request::header('referer', '/'); 375 | $referer_parts = parse_url($referer); 376 | $onboth = array_get($referer_parts, 'path'); 377 | if (array_get($referer_parts, 'query')) { 378 | $onboth .= '?' . array_get($referer_parts, 'query'); 379 | } 380 | 381 | if (!Input::get('code') && !Input::get('oauth_token')) { 382 | Session::put('mmanos.social.onsuccess', Input::get('onsuccess', $onboth)); 383 | Session::put('mmanos.social.onerror', Input::get('onerror', $onboth)); 384 | } 385 | 386 | if (!Auth::check()) { 387 | return Redirect::to(Session::pull('mmanos.social.onerror', '/')) 388 | ->with( 389 | Config::get('laravel-social::error_flash_var'), 390 | 'There was a problem connecting your account (1).' 391 | ); 392 | } 393 | 394 | if (Input::get('denied') || Input::get('error')) { 395 | return Redirect::to(Session::pull('mmanos.social.onerror', '/')) 396 | ->with( 397 | Config::get('laravel-social::error_flash_var'), 398 | 'There was a problem connecting your account (2).' 399 | ); 400 | } 401 | 402 | $provider = ucfirst($provider); 403 | 404 | try { 405 | $service = Social::service($provider); 406 | 407 | if (Config::get('laravel-social::providers.' . strtolower($provider) . '.offline')) { 408 | $service->setAccessType('offline'); 409 | } 410 | } catch (Exception $e) { 411 | App::abort(404); 412 | } 413 | 414 | if (2 === Social::oauthSpec($provider)) { 415 | return $this->oauth2Connect($provider, $service); 416 | } 417 | else { 418 | return $this->oauth1Connect($provider, $service); 419 | } 420 | } 421 | 422 | /** 423 | * Login to an OAuth2 service. 424 | * 425 | * @param string $provider 426 | * @param \OAuth\Common\Service\AbstractService $service 427 | * 428 | * @return Redirect 429 | */ 430 | protected function oauth2Connect($provider, $service) 431 | { 432 | if ($code = Input::get('code')) { 433 | try { 434 | $token = $service->requestAccessToken($code); 435 | } catch (Exception $e) { 436 | return Redirect::to(Session::pull('mmanos.social.onerror', '/')) 437 | ->with( 438 | Config::get('laravel-social::error_flash_var'), 439 | 'There was a problem connecting your account (3).' 440 | ); 441 | } 442 | 443 | return $this->processConnect($provider, $service, array( 444 | 'token' => $token->getAccessToken(), 445 | )); 446 | } 447 | 448 | return Redirect::to((string) $service->getAuthorizationUri()); 449 | } 450 | 451 | /** 452 | * Login to an OAuth1 consumer. 453 | * 454 | * @param string $provider 455 | * @param \OAuth\Common\Service\AbstractService $service 456 | * 457 | * @return Redirect 458 | */ 459 | protected function oauth1Connect($provider, $service) 460 | { 461 | if ($oauth_token = Input::get('oauth_token')) { 462 | try { 463 | $token = $service->requestAccessToken( 464 | $oauth_token, 465 | Input::get('oauth_verifier'), 466 | $service->getStorage()->retrieveAccessToken($provider)->getRequestTokenSecret() 467 | ); 468 | } catch (Exception $e) { 469 | return Redirect::to(Session::pull('mmanos.social.onerror', '/')) 470 | ->with( 471 | Config::get('laravel-social::error_flash_var'), 472 | 'There was a problem connecting your account (4).' 473 | ); 474 | } 475 | 476 | return $this->processConnect($provider, $service, array( 477 | 'token' => $token->getAccessToken(), 478 | 'secret' => $token->getAccessTokenSecret(), 479 | )); 480 | } 481 | 482 | try { 483 | // Extra request needed for oauth1 to get a request token. 484 | $token = $service->requestRequestToken(); 485 | } catch (Exception $e) { 486 | return Redirect::to(Session::pull('mmanos.social.onerror', '/')) 487 | ->with( 488 | Config::get('laravel-social::error_flash_var'), 489 | 'There was a problem connecting your account (5).' 490 | ); 491 | } 492 | 493 | return Redirect::to((string) $service->getAuthorizationUri(array( 494 | 'oauth_token' => $token->getRequestToken(), 495 | ))); 496 | } 497 | 498 | /** 499 | * Process the response from a provider connect attempt. 500 | * 501 | * @param string $provider 502 | * @param \OAuth\Common\Service\AbstractService $service 503 | * @param array $access_token 504 | * 505 | * @return Redirect 506 | */ 507 | protected function processConnect($provider, $service, $access_token) 508 | { 509 | $user_info_callback = Config::get( 510 | 'laravel-social::providers.' . strtolower($provider) . '.fetch_user_info' 511 | ); 512 | 513 | if (empty($user_info_callback) || !$user_info_callback instanceof Closure) { 514 | return Redirect::to(Session::pull('mmanos.social.onerror', '/')) 515 | ->with( 516 | Config::get('laravel-social::error_flash_var'), 517 | 'There was a problem connecting your account (6).' 518 | ); 519 | } 520 | 521 | try { 522 | $user_info = $user_info_callback($service); 523 | } catch (Exception $e) {} 524 | 525 | if (empty($user_info) || !is_array($user_info)) { 526 | return Redirect::to(Session::pull('mmanos.social.onerror', '/')) 527 | ->with( 528 | Config::get('laravel-social::error_flash_var'), 529 | 'There was a problem connecting your account (7).' 530 | ); 531 | } 532 | 533 | if (empty($user_info['id'])) { 534 | return Redirect::to(Session::pull('mmanos.social.onerror', '/')) 535 | ->with( 536 | Config::get('laravel-social::error_flash_var'), 537 | 'There was a problem connecting your account (8).' 538 | ); 539 | } 540 | 541 | $provider_id = array_get($user_info, 'id'); 542 | 543 | $user_provider = Provider::where('provider', strtolower($provider)) 544 | ->where('provider_id', $provider_id) 545 | ->first(); 546 | 547 | if ($user_provider) { 548 | if ($user_provider->user_id != Auth::id()) { 549 | return Redirect::to(Session::pull('mmanos.social.onerror', '/')) 550 | ->with( 551 | Config::get('laravel-social::error_flash_var'), 552 | 'There was a problem connecting your account (9).' 553 | ); 554 | } 555 | 556 | $user_provider->access_token = $access_token; 557 | $user_provider->save(); 558 | } 559 | else { 560 | $this->linkProvider(Auth::id(), $provider, $provider_id, $access_token); 561 | } 562 | 563 | return Redirect::to(Session::pull('mmanos.social.onsuccess', '/')) 564 | ->with( 565 | Config::get('laravel-social::success_flash_var'), 566 | 'You have successfully connected your account.' 567 | ); 568 | } 569 | 570 | /** 571 | * Link the give user to the given provider. 572 | * 573 | * @param integer $user_id 574 | * @param string $provider 575 | * @param integer $provider_id 576 | * @param array $access_token 577 | * 578 | * @return Provider 579 | */ 580 | protected function linkProvider($user_id, $provider, $provider_id, $access_token) 581 | { 582 | $user_provider = new Provider; 583 | $user_provider->user_id = $user_id; 584 | $user_provider->provider = strtolower($provider); 585 | $user_provider->provider_id = $provider_id; 586 | $user_provider->access_token = $access_token; 587 | $user_provider->save(); 588 | 589 | return $user_provider; 590 | } 591 | } 592 | -------------------------------------------------------------------------------- /src/lang/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmanos/laravel-social/f3ec7165509825d16cb9d06759a77580a291cb83/src/lang/.gitkeep -------------------------------------------------------------------------------- /src/migrations/2014_09_04_000000_laravel_social_create_providers_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 19 | $table->integer('user_id'); 20 | $table->string('provider'); 21 | $table->string('provider_id'); 22 | $table->text('access_token'); 23 | $table->timestamps(); 24 | 25 | $table->unique(array('provider', 'provider_id')); 26 | $table->index(array('user_id', 'provider')); 27 | }); 28 | } 29 | 30 | /** 31 | * Reverse the migrations. 32 | * 33 | * @return void 34 | */ 35 | public function down() 36 | { 37 | $table_name = Config::get('laravel-social::table', 'user_providers'); 38 | 39 | Schema::drop($table_name); 40 | } 41 | } -------------------------------------------------------------------------------- /src/models/Provider.php: -------------------------------------------------------------------------------- 1 | attributes['access_token'] = Crypt::encrypt(json_encode($value)); 27 | } 28 | 29 | public function request($path, $method = 'GET', $body = null, array $extra_headers = array()) 30 | { 31 | $access_token = $this->access_token; 32 | 33 | $service = Facades\Social::service($this->provider); 34 | 35 | if (2 === Facades\Social::oauthSpec($this->provider)) { 36 | $token = new StdOAuth2Token; 37 | $token->setAccessToken(array_get($access_token, 'token')); 38 | } 39 | else { 40 | $token = new StdOAuth1Token; 41 | $token->setAccessToken(array_get($access_token, 'token')); 42 | $token->setAccessTokenSecret(array_get($access_token, 'secret')); 43 | } 44 | 45 | $service->getStorage()->storeAccessToken(ucfirst($this->provider), $token); 46 | 47 | try { 48 | return $service->request($path, $method, $body, $extra_headers); 49 | } catch (\OAuth\Common\Http\Exception\TokenResponseException $e) { 50 | if ($this->refreshAccessToken()) { 51 | return $service->request($path, $method, $body, $extra_headers); 52 | } 53 | 54 | throw $e; 55 | } 56 | } 57 | 58 | public function refreshAccessToken() 59 | { 60 | $access_token = $this->access_token; 61 | 62 | $service = Facades\Social::service($this->provider); 63 | 64 | if (2 === Facades\Social::oauthSpec($this->provider)) { 65 | $token = new StdOAuth2Token; 66 | $token->setAccessToken(array_get($access_token, 'token')); 67 | $token->setRefreshToken(array_get($access_token, 'refresh_token')); 68 | } 69 | else { 70 | $token = new StdOAuth1Token; 71 | $token->setAccessToken(array_get($access_token, 'token')); 72 | $token->setAccessTokenSecret(array_get($access_token, 'secret')); 73 | $token->setRefreshToken(array_get($access_token, 'refresh_token')); 74 | } 75 | 76 | $service->getStorage()->storeAccessToken(ucfirst($this->provider), $token); 77 | 78 | try { 79 | $new_token = $service->refreshAccessToken($token); 80 | } catch (\Exception $e) { 81 | return false; 82 | } 83 | 84 | if (!$new_token->getAccessToken()) { 85 | return false; 86 | } 87 | 88 | $access_token['token'] = $new_token->getAccessToken(); 89 | if ($new_token->getEndOfLife()) { 90 | $access_token['end_of_life'] = $new_token->getEndOfLife(); 91 | } 92 | if ($new_token->getExtraParams()) { 93 | $access_token['extra_params'] = $new_token->getExtraParams(); 94 | } 95 | if (2 !== Facades\Social::oauthSpec($this->provider) && $new_token->getAccessTokenSecret()) { 96 | $access_token['secret'] = $new_token->getAccessTokenSecret(); 97 | } 98 | 99 | $this->access_token = $access_token; 100 | $this->save(); 101 | 102 | return true; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/views/social/complete.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

Please complete your profile...

5 | 6 | {{ Form::open(array('action' => 'Mmanos\Social\SocialController@postComplete')) }} 7 |
8 | @foreach ($failed_fields as $idx => $field) 9 |
10 | {{ Form::label($field, ucwords(str_replace(array('-', '_'), ' ', $field))) }} 11 | 12 | @if ($idx === 0) 13 | {{ Form::text($field, Input::old($field, array_get($info, $field)), array('autofocus' => 'autofocus', 'class' => 'form-control')) }} 14 | @else 15 | {{ Form::text($field, Input::old($field, array_get($info, $field)), array('class' => 'form-control')) }} 16 | @endif 17 | 18 | {{ $errors->first($field, ':message') }} 19 |
20 | @endforeach 21 | 22 |
23 | {{ Form::submit('Save', array('class' => 'btn btn-primary')) }} 24 |
25 |
26 | {{ Form::close() }} 27 |
28 |
29 |
30 | -------------------------------------------------------------------------------- /tests/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmanos/laravel-social/f3ec7165509825d16cb9d06759a77580a291cb83/tests/.gitkeep --------------------------------------------------------------------------------