├── LICENSE.md ├── app ├── OAuth │ ├── Bridge │ │ ├── Grant │ │ │ └── SocialGrant.php │ │ └── Repositories │ │ │ └── ScopeRepository.php │ ├── SocialUserProvider.php │ └── SocialUserProviderInterface.php ├── Providers │ └── OAuthServiceProvider.php └── Repositories │ └── UserRepository.php └── config ├── app.php ├── auth.php └── services.php /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Orobo Lucky Ozoka 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app/OAuth/Bridge/Grant/SocialGrant.php: -------------------------------------------------------------------------------- 1 | socialUserProvider = $socialUserProvider; 38 | $this->setRefreshTokenRepository($refreshTokenRepository); 39 | $this->refreshTokenTTL = new \DateInterval('P1M'); 40 | } 41 | 42 | /** 43 | * {@inheritdoc} 44 | */ 45 | public function respondToAccessTokenRequest( 46 | ServerRequestInterface $request, 47 | ResponseTypeInterface $responseType, 48 | DateInterval $accessTokenTTL 49 | ) { 50 | $client = $this->validateClient($request); 51 | 52 | $scopes = $this->validateScopes($this->getRequestParameter('scope', $request, $this->defaultScope)); 53 | 54 | $user = $this->validateUser($request); 55 | 56 | // Finalize the requested scopes 57 | $finalizedScopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client, $user->getIdentifier()); 58 | 59 | // Issue and persist new tokens 60 | $accessToken = $this->issueAccessToken($accessTokenTTL, $client, $user->getIdentifier(), $finalizedScopes); 61 | $refreshToken = $this->issueRefreshToken($accessToken); 62 | 63 | // Send events to emitter 64 | $this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request)); 65 | $this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request)); 66 | 67 | // Inject tokens into response 68 | $responseType->setAccessToken($accessToken); 69 | $responseType->setRefreshToken($refreshToken); 70 | 71 | return $responseType; 72 | } 73 | 74 | /** 75 | * Validate the user. 76 | * 77 | * @param ServerRequestInterface $request 78 | * 79 | * @throws OAuthServerException 80 | * 81 | * @return UserEntityInterface 82 | */ 83 | protected function validateUser(ServerRequestInterface $request) 84 | { 85 | $provider = $this->getRequestParameter('provider', $request); 86 | 87 | if (is_null($provider)) { 88 | throw OAuthServerException::invalidRequest('provider'); 89 | } 90 | 91 | if (! $this->isProviderSupported($provider)) { 92 | throw OAuthServerException::invalidRequest('provider', 'Invalid provider'); 93 | } 94 | 95 | $accessToken = $this->getRequestParameter('access_token', $request); 96 | 97 | if (is_null($accessToken)) { 98 | throw OAuthServerException::invalidRequest('access_token'); 99 | } 100 | 101 | // Get user from social network provider 102 | $user = $this->socialUserProvider->getUserEntityByAccessToken( 103 | $provider, 104 | $accessToken 105 | ); 106 | 107 | if ($user instanceof UserEntityInterface === false) { 108 | $this->getEmitter()->emit(new RequestEvent(RequestEvent::USER_AUTHENTICATION_FAILED, $request)); 109 | 110 | throw OAuthServerException::invalidCredentials(); 111 | } 112 | 113 | return $user; 114 | } 115 | 116 | /** 117 | * Determine if the provider is supported. 118 | * 119 | * @param string $provider 120 | * @return bool 121 | */ 122 | protected function isProviderSupported($provider) 123 | { 124 | return in_array($provider, config('auth.social.providers')); 125 | } 126 | 127 | /** 128 | * {@inheritdoc} 129 | */ 130 | public function getIdentifier() 131 | { 132 | return 'social'; 133 | } 134 | } -------------------------------------------------------------------------------- /app/OAuth/Bridge/Repositories/ScopeRepository.php: -------------------------------------------------------------------------------- 1 | filter(function ($scope) { 19 | return Passport::hasScope($scope->getIdentifier()); 20 | })->values()->all(); 21 | } 22 | } -------------------------------------------------------------------------------- /app/OAuth/SocialUserProvider.php: -------------------------------------------------------------------------------- 1 | userRepository = $userRepository; 27 | } 28 | 29 | /** 30 | * {@inheritdoc} 31 | */ 32 | public function getUserEntityByAccessToken($provider, $accessToken) 33 | { 34 | $user = $this->getUserFromSocialProvider($provider, $accessToken); 35 | 36 | if (! $user) { 37 | return; 38 | } 39 | 40 | return new UserEntity($user->getAuthIdentifier()); 41 | } 42 | 43 | /** 44 | * Get the user from the specified provider using the given access token. 45 | * 46 | * @param string $provider 47 | * @param string $accessToken 48 | * 49 | * @throws \League\OAuth2\Server\Exception\OAuthServerException 50 | * 51 | * @return \App\User 52 | */ 53 | public function getUserFromSocialProvider($provider, $accessToken) 54 | { 55 | try { 56 | $user = Socialite::driver($provider)->userFromToken($accessToken); 57 | } catch (\Exception $ex) { 58 | throw new OAuthServerException( 59 | 'Authentication error, invalid access token', 60 | $errorCode = 400, 61 | 'invalid_request' 62 | ); 63 | } 64 | 65 | return $this->userRepository->findOrCreateSocialUser( 66 | array_merge($user->getRaw(), ['provider' => $provider]) 67 | ); 68 | } 69 | } -------------------------------------------------------------------------------- /app/OAuth/SocialUserProviderInterface.php: -------------------------------------------------------------------------------- 1 | app->extend(AuthorizationServer::class, function ($server, $app) { 24 | return tap($server, function ($server) { 25 | $server->enableGrantType( 26 | $grantType = $this->makeSocialGrant(), Passport::tokensExpireIn() 27 | ); 28 | 29 | // Allow all scopes to be requested for this grant 30 | $grantType->setScopeRepository( 31 | $this->app->make(ScopeRepository::class) 32 | ); 33 | }); 34 | }); 35 | 36 | $this->app->singleton(SocialUserProviderInterface::class, SocialUserProvider::class); 37 | } 38 | 39 | /** 40 | * Create and configure and instance of Social Grant. 41 | * 42 | * @return \App\OAuth\Bridge\Grant\SocialGrant 43 | */ 44 | protected function makeSocialGrant() 45 | { 46 | $grant = new SocialGrant( 47 | $this->app->make(SocialUserProviderInterface::class), 48 | $this->app->make(RefreshTokenRepository::class) 49 | ); 50 | 51 | $grant->setRefreshTokenTTL(Passport::refreshTokensExpireIn()); 52 | 53 | return $grant; 54 | } 55 | 56 | /** 57 | * Bootstrap any application services. 58 | * 59 | * @return void 60 | */ 61 | public function boot() 62 | { 63 | // 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/Repositories/UserRepository.php: -------------------------------------------------------------------------------- 1 | model = $model; 22 | } 23 | 24 | /** 25 | * Retrieve or create a new resource owner. 26 | * 27 | * @param array $attributes 28 | * @return App\User 29 | */ 30 | public function findOrCreateSocialUser(array $attributes) 31 | { 32 | return $this->model->firstOrCreate( 33 | ['provider_id' => $attributes['id']], 34 | $attributes 35 | ); 36 | } 37 | } -------------------------------------------------------------------------------- /config/app.php: -------------------------------------------------------------------------------- 1 | env('APP_NAME', 'Laravel'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Application Environment 21 | |-------------------------------------------------------------------------- 22 | | 23 | | This value determines the "environment" your application is currently 24 | | running in. This may determine how you prefer to configure various 25 | | services the application utilizes. Set this in your ".env" file. 26 | | 27 | */ 28 | 29 | 'env' => env('APP_ENV', 'production'), 30 | 31 | /* 32 | |-------------------------------------------------------------------------- 33 | | Application Debug Mode 34 | |-------------------------------------------------------------------------- 35 | | 36 | | When your application is in debug mode, detailed error messages with 37 | | stack traces will be shown on every error that occurs within your 38 | | application. If disabled, a simple generic error page is shown. 39 | | 40 | */ 41 | 42 | 'debug' => env('APP_DEBUG', false), 43 | 44 | /* 45 | |-------------------------------------------------------------------------- 46 | | Application URL 47 | |-------------------------------------------------------------------------- 48 | | 49 | | This URL is used by the console to properly generate URLs when using 50 | | the Artisan command line tool. You should set this to the root of 51 | | your application so that it is used when running Artisan tasks. 52 | | 53 | */ 54 | 55 | 'url' => env('APP_URL', 'http://localhost'), 56 | 57 | 'asset_url' => env('ASSET_URL', null), 58 | 59 | /* 60 | |-------------------------------------------------------------------------- 61 | | Application Timezone 62 | |-------------------------------------------------------------------------- 63 | | 64 | | Here you may specify the default timezone for your application, which 65 | | will be used by the PHP date and date-time functions. We have gone 66 | | ahead and set this to a sensible default for you out of the box. 67 | | 68 | */ 69 | 70 | 'timezone' => 'UTC', 71 | 72 | /* 73 | |-------------------------------------------------------------------------- 74 | | Application Locale Configuration 75 | |-------------------------------------------------------------------------- 76 | | 77 | | The application locale determines the default locale that will be used 78 | | by the translation service provider. You are free to set this value 79 | | to any of the locales which will be supported by the application. 80 | | 81 | */ 82 | 83 | 'locale' => 'en', 84 | 85 | /* 86 | |-------------------------------------------------------------------------- 87 | | Application Fallback Locale 88 | |-------------------------------------------------------------------------- 89 | | 90 | | The fallback locale determines the locale to use when the current one 91 | | is not available. You may change the value to correspond to any of 92 | | the language folders that are provided through your application. 93 | | 94 | */ 95 | 96 | 'fallback_locale' => 'en', 97 | 98 | /* 99 | |-------------------------------------------------------------------------- 100 | | Faker Locale 101 | |-------------------------------------------------------------------------- 102 | | 103 | | This locale will be used by the Faker PHP library when generating fake 104 | | data for your database seeds. For example, this will be used to get 105 | | localized telephone numbers, street address information and more. 106 | | 107 | */ 108 | 109 | 'faker_locale' => 'en_US', 110 | 111 | /* 112 | |-------------------------------------------------------------------------- 113 | | Encryption Key 114 | |-------------------------------------------------------------------------- 115 | | 116 | | This key is used by the Illuminate encrypter service and should be set 117 | | to a random, 32 character string, otherwise these encrypted strings 118 | | will not be safe. Please do this before deploying an application! 119 | | 120 | */ 121 | 122 | 'key' => env('APP_KEY'), 123 | 124 | 'cipher' => 'AES-256-CBC', 125 | 126 | /* 127 | |-------------------------------------------------------------------------- 128 | | Autoloaded Service Providers 129 | |-------------------------------------------------------------------------- 130 | | 131 | | The service providers listed here will be automatically loaded on the 132 | | request to your application. Feel free to add your own services to 133 | | this array to grant expanded functionality to your applications. 134 | | 135 | */ 136 | 137 | 'providers' => [ 138 | 139 | /* 140 | * Laravel Framework Service Providers... 141 | */ 142 | Illuminate\Auth\AuthServiceProvider::class, 143 | Illuminate\Broadcasting\BroadcastServiceProvider::class, 144 | Illuminate\Bus\BusServiceProvider::class, 145 | Illuminate\Cache\CacheServiceProvider::class, 146 | Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class, 147 | Illuminate\Cookie\CookieServiceProvider::class, 148 | Illuminate\Database\DatabaseServiceProvider::class, 149 | Illuminate\Encryption\EncryptionServiceProvider::class, 150 | Illuminate\Filesystem\FilesystemServiceProvider::class, 151 | Illuminate\Foundation\Providers\FoundationServiceProvider::class, 152 | Illuminate\Hashing\HashServiceProvider::class, 153 | Illuminate\Mail\MailServiceProvider::class, 154 | Illuminate\Notifications\NotificationServiceProvider::class, 155 | Illuminate\Pagination\PaginationServiceProvider::class, 156 | Illuminate\Pipeline\PipelineServiceProvider::class, 157 | Illuminate\Queue\QueueServiceProvider::class, 158 | Illuminate\Redis\RedisServiceProvider::class, 159 | Illuminate\Auth\Passwords\PasswordResetServiceProvider::class, 160 | Illuminate\Session\SessionServiceProvider::class, 161 | Illuminate\Translation\TranslationServiceProvider::class, 162 | Illuminate\Validation\ValidationServiceProvider::class, 163 | Illuminate\View\ViewServiceProvider::class, 164 | 165 | /* 166 | * Package Service Providers... 167 | */ 168 | 169 | /* 170 | * Application Service Providers... 171 | */ 172 | App\Providers\AppServiceProvider::class, 173 | App\Providers\AuthServiceProvider::class, 174 | // App\Providers\BroadcastServiceProvider::class, 175 | App\Providers\EventServiceProvider::class, 176 | App\Providers\RouteServiceProvider::class, 177 | App\Providers\OAuthServiceProvider::class 178 | 179 | ], 180 | 181 | /* 182 | |-------------------------------------------------------------------------- 183 | | Class Aliases 184 | |-------------------------------------------------------------------------- 185 | | 186 | | This array of class aliases will be registered when this application 187 | | is started. However, feel free to register as many as you wish as 188 | | the aliases are "lazy" loaded so they don't hinder performance. 189 | | 190 | */ 191 | 192 | 'aliases' => [ 193 | 194 | 'App' => Illuminate\Support\Facades\App::class, 195 | 'Arr' => Illuminate\Support\Arr::class, 196 | 'Artisan' => Illuminate\Support\Facades\Artisan::class, 197 | 'Auth' => Illuminate\Support\Facades\Auth::class, 198 | 'Blade' => Illuminate\Support\Facades\Blade::class, 199 | 'Broadcast' => Illuminate\Support\Facades\Broadcast::class, 200 | 'Bus' => Illuminate\Support\Facades\Bus::class, 201 | 'Cache' => Illuminate\Support\Facades\Cache::class, 202 | 'Config' => Illuminate\Support\Facades\Config::class, 203 | 'Cookie' => Illuminate\Support\Facades\Cookie::class, 204 | 'Crypt' => Illuminate\Support\Facades\Crypt::class, 205 | 'DB' => Illuminate\Support\Facades\DB::class, 206 | 'Eloquent' => Illuminate\Database\Eloquent\Model::class, 207 | 'Event' => Illuminate\Support\Facades\Event::class, 208 | 'File' => Illuminate\Support\Facades\File::class, 209 | 'Gate' => Illuminate\Support\Facades\Gate::class, 210 | 'Hash' => Illuminate\Support\Facades\Hash::class, 211 | 'Lang' => Illuminate\Support\Facades\Lang::class, 212 | 'Log' => Illuminate\Support\Facades\Log::class, 213 | 'Mail' => Illuminate\Support\Facades\Mail::class, 214 | 'Notification' => Illuminate\Support\Facades\Notification::class, 215 | 'Password' => Illuminate\Support\Facades\Password::class, 216 | 'Queue' => Illuminate\Support\Facades\Queue::class, 217 | 'Redirect' => Illuminate\Support\Facades\Redirect::class, 218 | 'Redis' => Illuminate\Support\Facades\Redis::class, 219 | 'Request' => Illuminate\Support\Facades\Request::class, 220 | 'Response' => Illuminate\Support\Facades\Response::class, 221 | 'Route' => Illuminate\Support\Facades\Route::class, 222 | 'Schema' => Illuminate\Support\Facades\Schema::class, 223 | 'Session' => Illuminate\Support\Facades\Session::class, 224 | 'Storage' => Illuminate\Support\Facades\Storage::class, 225 | 'Str' => Illuminate\Support\Str::class, 226 | 'URL' => Illuminate\Support\Facades\URL::class, 227 | 'Validator' => Illuminate\Support\Facades\Validator::class, 228 | 'View' => Illuminate\Support\Facades\View::class, 229 | 230 | ], 231 | 232 | ]; 233 | -------------------------------------------------------------------------------- /config/auth.php: -------------------------------------------------------------------------------- 1 | [ 17 | 'guard' => 'web', 18 | 'passwords' => 'users', 19 | ], 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | Authentication Guards 24 | |-------------------------------------------------------------------------- 25 | | 26 | | Next, you may define every authentication guard for your application. 27 | | Of course, a great default configuration has been defined for you 28 | | here which uses session storage and the Eloquent user provider. 29 | | 30 | | All authentication drivers have a user provider. This defines how the 31 | | users are actually retrieved out of your database or other storage 32 | | mechanisms used by this application to persist your user's data. 33 | | 34 | | Supported: "session", "token" 35 | | 36 | */ 37 | 38 | 'guards' => [ 39 | 'web' => [ 40 | 'driver' => 'session', 41 | 'provider' => 'users', 42 | ], 43 | 44 | 'api' => [ 45 | 'driver' => 'passport', 46 | 'provider' => 'users', 47 | 'hash' => false, 48 | ], 49 | ], 50 | 51 | /* 52 | |-------------------------------------------------------------------------- 53 | | User Providers 54 | |-------------------------------------------------------------------------- 55 | | 56 | | All authentication drivers have a user provider. This defines how the 57 | | users are actually retrieved out of your database or other storage 58 | | mechanisms used by this application to persist your user's data. 59 | | 60 | | If you have multiple user tables or models you may configure multiple 61 | | sources which represent each model / table. These sources may then 62 | | be assigned to any extra authentication guards you have defined. 63 | | 64 | | Supported: "database", "eloquent" 65 | | 66 | */ 67 | 68 | 'providers' => [ 69 | 'users' => [ 70 | 'driver' => 'eloquent', 71 | 'model' => App\User::class, 72 | ], 73 | 74 | // 'users' => [ 75 | // 'driver' => 'database', 76 | // 'table' => 'users', 77 | // ], 78 | ], 79 | 80 | /* 81 | |-------------------------------------------------------------------------- 82 | | Resetting Passwords 83 | |-------------------------------------------------------------------------- 84 | | 85 | | You may specify multiple password reset configurations if you have more 86 | | than one user table or model in the application and you want to have 87 | | separate password reset settings based on the specific user types. 88 | | 89 | | The expire time is the number of minutes that the reset token should be 90 | | considered valid. This security feature keeps tokens short-lived so 91 | | they have less time to be guessed. You may change this as needed. 92 | | 93 | */ 94 | 95 | 'passwords' => [ 96 | 'users' => [ 97 | 'provider' => 'users', 98 | 'table' => 'password_resets', 99 | 'expire' => 60, 100 | ], 101 | ], 102 | 103 | 'social' => [ 104 | 'providers' => [ 105 | 'google', 'facebook' 106 | ] 107 | ] 108 | 109 | ]; 110 | -------------------------------------------------------------------------------- /config/services.php: -------------------------------------------------------------------------------- 1 | [ 18 | 'domain' => env('MAILGUN_DOMAIN'), 19 | 'secret' => env('MAILGUN_SECRET'), 20 | 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'), 21 | ], 22 | 23 | 'postmark' => [ 24 | 'token' => env('POSTMARK_TOKEN'), 25 | ], 26 | 27 | 'ses' => [ 28 | 'key' => env('AWS_ACCESS_KEY_ID'), 29 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 30 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 31 | ], 32 | 33 | 'sparkpost' => [ 34 | 'secret' => env('SPARKPOST_SECRET'), 35 | ], 36 | 37 | 'stripe' => [ 38 | 'model' => App\User::class, 39 | 'key' => env('STRIPE_KEY'), 40 | 'secret' => env('STRIPE_SECRET'), 41 | 'webhook' => [ 42 | 'secret' => env('STRIPE_WEBHOOK_SECRET'), 43 | 'tolerance' => env('STRIPE_WEBHOOK_TOLERANCE', 300), 44 | ], 45 | ], 46 | 47 | 'google' => [ 48 | 'client_id' => env('GOOGLE_CLIENT_ID'), 49 | 'client_secret' => env('GOOGLE_CLIENT_SECRET'), 50 | 'redirect' => null, 51 | ], 52 | 53 | 'facebook' => [ 54 | 'client_id' => env('FACEBOOK_CLIENT_ID'), 55 | 'client_secret' => env('FACEBOOK_CLIENT_SECRET'), 56 | 'redirect' => null, 57 | ], 58 | ]; 59 | --------------------------------------------------------------------------------