Add OAuth client

This commit is contained in:
Pig Fang 2019-12-15 11:19:10 +08:00
parent 25a7134d63
commit 91fbb42431
14 changed files with 372 additions and 32 deletions

View File

@ -11,6 +11,7 @@ use App\Rules\Captcha;
use Auth;
use Cache;
use Illuminate\Http\Request;
use Laravel\Socialite\Facades\Socialite;
use Mail;
use Session;
use URL;
@ -301,4 +302,44 @@ class AuthController extends Controller
{
return json(['token' => Auth::guard('jwt')->refresh()]);
}
public function oauthLogin($driver)
{
return Socialite::driver($driver)->redirect();
}
public function oauthCallback($driver)
{
$remoteUser = Socialite::driver($driver)->user();
$email = $remoteUser->email;
if (empty($email)) {
abort(500, 'Unsupported OAuth Server which does not provide email.');
}
$user = User::where('email', $email)->first();
if ($user) {
event(new Events\UserLoggedIn($user));
Auth::login($user);
} else {
$user = new User();
$user->email = $email;
$user->nickname = $remoteUser->nickname ?? $remoteUser->name ?? $email;
$user->score = option('user_initial_score');
$user->avatar = 0;
$user->password = '';
$user->ip = get_client_ip();
$user->permission = User::NORMAL;
$user->register_at = get_datetime_string();
$user->last_sign_at = get_datetime_string(time() - 86400);
$user->save();
event(new Events\UserRegistered($user));
Auth::login($user);
}
return redirect('/user');
}
}

View File

@ -23,6 +23,9 @@ class AppServiceProvider extends ServiceProvider
$this->app->singleton('parsedown', \Parsedown::class);
$this->app->singleton(\App\Services\Webpack::class);
$this->app->singleton(\App\Services\Filter::class);
$this->app->singleton('oauth.providers', function () {
return new \Illuminate\Support\Collection();
});
}
/**

View File

@ -83,5 +83,9 @@ class ViewServiceProvider extends ServiceProvider
],
]);
});
View::composer('auth.oauth', function ($view) {
$view->with('providers', resolve('oauth.providers'));
});
}
}

View File

@ -4,45 +4,46 @@
"license": "MIT",
"require": {
"php": ">=7.2.0",
"ext-zip": "*",
"ext-ctype": "*",
"ext-gd": "*",
"ext-json": "*",
"ext-mbstring": "*",
"ext-openssl": "*",
"ext-pdo": "*",
"ext-mbstring": "*",
"ext-tokenizer": "*",
"ext-gd": "*",
"ext-xml": "*",
"ext-ctype": "*",
"ext-json": "*",
"predis/predis": "~1.0",
"doctrine/inflector": "1.1.0",
"laravel/framework": "6.*",
"nesbot/carbon": "^2.0",
"ext-zip": "*",
"composer/ca-bundle": "^1.2",
"composer/semver": "^1.4",
"doctrine/dbal": "^2.9",
"doctrine/inflector": "1.1.0",
"facade/ignition": "^1.4",
"gregwar/captcha": "1.*",
"guzzlehttp/guzzle": "^6.3",
"doctrine/dbal": "^2.9",
"tymon/jwt-auth": "dev-develop",
"laravel/framework": "6.*",
"laravel/passport": "^7.3",
"composer/ca-bundle": "^1.2",
"facade/ignition": "^1.4",
"nesbot/carbon": "^2.0",
"predis/predis": "~1.0",
"rcrowe/twigbridge": "^0.11.1",
"socialiteproviders/manager": "^3.4",
"spatie/laravel-translation-loader": "^2.4",
"symfony/process": "^4.4",
"symfony/yaml": "^4.3",
"twig/twig": "^2.11",
"rcrowe/twigbridge": "^0.11.1"
"tymon/jwt-auth": "dev-develop"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.2",
"barryvdh/laravel-ide-helper": "^2.6",
"beyondcode/laravel-dump-server": "^1.2",
"fzaninotto/faker": "~1.8",
"laravel/browser-kit-testing": "~5.0",
"laravel/tinker": "^1.0",
"mockery/mockery": "^1.2.2",
"phpdocumentor/reflection-docblock": "3.2.2",
"phpunit/phpunit": "~8.0",
"laravel/browser-kit-testing": "~5.0",
"laravel/tinker": "^1.0",
"barryvdh/laravel-debugbar": "^3.2",
"beyondcode/laravel-dump-server": "^1.2",
"symfony/dom-crawler": "^4.3",
"symfony/css-selector": "^4.3",
"barryvdh/laravel-ide-helper": "^2.6"
"symfony/dom-crawler": "^4.3"
},
"autoload": {
"classmap": [
@ -72,6 +73,13 @@
"preferred-install": "dist",
"sort-packages": true
},
"extra": {
"laravel": {
"dont-discover": [
"laravel/socialite"
]
}
},
"repositories": {
"packagist": {
"type": "composer",

206
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "5cc00172e30620b65fb629cecea98e5b",
"content-hash": "1d98f570b7b67b1ecd3b72dfcec58e7b",
"packages": [
{
"name": "composer/ca-bundle",
@ -1459,6 +1459,70 @@
],
"time": "2019-10-08T16:45:24+00:00"
},
{
"name": "laravel/socialite",
"version": "v4.3.1",
"source": {
"type": "git",
"url": "https://github.com/laravel/socialite.git",
"reference": "2d670d5b100ef2dc72dc578126b2b97985791f52"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/socialite/zipball/2d670d5b100ef2dc72dc578126b2b97985791f52",
"reference": "2d670d5b100ef2dc72dc578126b2b97985791f52",
"shasum": ""
},
"require": {
"ext-json": "*",
"guzzlehttp/guzzle": "~6.0",
"illuminate/http": "~5.7.0|~5.8.0|^6.0|^7.0",
"illuminate/support": "~5.7.0|~5.8.0|^6.0|^7.0",
"league/oauth1-client": "~1.0",
"php": "^7.1.3"
},
"require-dev": {
"illuminate/contracts": "~5.7.0|~5.8.0|^6.0|^7.0",
"mockery/mockery": "^1.0",
"phpunit/phpunit": "^7.0|^8.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.0-dev"
},
"laravel": {
"providers": [
"Laravel\\Socialite\\SocialiteServiceProvider"
],
"aliases": {
"Socialite": "Laravel\\Socialite\\Facades\\Socialite"
}
}
},
"autoload": {
"psr-4": {
"Laravel\\Socialite\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
}
],
"description": "Laravel wrapper around OAuth 1 & OAuth 2 libraries.",
"homepage": "https://laravel.com",
"keywords": [
"laravel",
"oauth"
],
"time": "2019-11-26T17:39:15+00:00"
},
{
"name": "lcobucci/jwt",
"version": "3.3.1",
@ -1648,6 +1712,69 @@
],
"time": "2019-12-08T21:46:50+00:00"
},
{
"name": "league/oauth1-client",
"version": "1.7.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/oauth1-client.git",
"reference": "fca5f160650cb74d23fc11aa570dd61f86dcf647"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/fca5f160650cb74d23fc11aa570dd61f86dcf647",
"reference": "fca5f160650cb74d23fc11aa570dd61f86dcf647",
"shasum": ""
},
"require": {
"guzzlehttp/guzzle": "^6.0",
"php": ">=5.5.0"
},
"require-dev": {
"mockery/mockery": "^0.9",
"phpunit/phpunit": "^4.0",
"squizlabs/php_codesniffer": "^2.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
"psr-4": {
"League\\OAuth1\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ben Corlett",
"email": "bencorlett@me.com",
"homepage": "http://www.webcomm.com.au",
"role": "Developer"
}
],
"description": "OAuth 1.0 Client Library",
"keywords": [
"Authentication",
"SSO",
"authorization",
"bitbucket",
"identity",
"idp",
"oauth",
"oauth1",
"single sign on",
"trello",
"tumblr",
"twitter"
],
"time": "2016-08-17T00:36:58+00:00"
},
{
"name": "league/oauth2-server",
"version": "7.4.0",
@ -2047,16 +2174,16 @@
},
{
"name": "phpoption/phpoption",
"version": "1.7.0",
"version": "1.7.1",
"source": {
"type": "git",
"url": "https://github.com/schmittjoh/php-option.git",
"reference": "318ad673ba36e53e10ca510aec1c54b4f2a5f2ae"
"reference": "100a25207566930efd926cf205542946aa692e01"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/schmittjoh/php-option/zipball/318ad673ba36e53e10ca510aec1c54b4f2a5f2ae",
"reference": "318ad673ba36e53e10ca510aec1c54b4f2a5f2ae",
"url": "https://api.github.com/repos/schmittjoh/php-option/zipball/100a25207566930efd926cf205542946aa692e01",
"reference": "100a25207566930efd926cf205542946aa692e01",
"shasum": ""
},
"require": {
@ -2098,7 +2225,7 @@
"php",
"type"
],
"time": "2019-12-13T00:10:22+00:00"
"time": "2019-12-14T13:46:39+00:00"
},
{
"name": "phpseclib/phpseclib",
@ -2755,6 +2882,63 @@
],
"time": "2019-12-13T21:54:06+00:00"
},
{
"name": "socialiteproviders/manager",
"version": "v3.4.3",
"source": {
"type": "git",
"url": "https://github.com/SocialiteProviders/Manager.git",
"reference": "09903d33429f9f6c0da32c545c036a3e18964bbf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/SocialiteProviders/Manager/zipball/09903d33429f9f6c0da32c545c036a3e18964bbf",
"reference": "09903d33429f9f6c0da32c545c036a3e18964bbf",
"shasum": ""
},
"require": {
"illuminate/support": "~5.4|~5.7.0|~5.8.0|^6.0",
"laravel/socialite": "~3.0|~4.0",
"php": "^5.6 || ^7.0"
},
"require-dev": {
"mockery/mockery": "^0.9.4",
"phpunit/phpunit": "^5.0"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"SocialiteProviders\\Manager\\ServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"SocialiteProviders\\Manager\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Andy Wendt",
"email": "andy@awendt.com"
},
{
"name": "Anton Komarev",
"email": "a.komarev@cybercog.su"
},
{
"name": "Miguel Piedrafita",
"email": "soy@miguelpiedrafita.com"
}
],
"description": "Easily add new or override built-in providers in Laravel Socialite.",
"time": "2019-09-25T06:06:35+00:00"
},
{
"name": "spatie/laravel-translation-loader",
"version": "2.4.0",
@ -7557,15 +7741,15 @@
"prefer-lowest": false,
"platform": {
"php": ">=7.2.0",
"ext-zip": "*",
"ext-ctype": "*",
"ext-gd": "*",
"ext-json": "*",
"ext-mbstring": "*",
"ext-openssl": "*",
"ext-pdo": "*",
"ext-mbstring": "*",
"ext-tokenizer": "*",
"ext-gd": "*",
"ext-xml": "*",
"ext-ctype": "*",
"ext-json": "*"
"ext-zip": "*"
},
"platform-dev": []
}

View File

@ -42,6 +42,7 @@ rotation: Rotation
pause: Pause
reset: Reset
more: More
submit: Submit
cancel: Cancel
yes: Yes

View File

@ -15,6 +15,7 @@
- Added badge "STAFF" for administrators.
- Added badges at texture detail page.
- Added FAQ link at error page.
- Added login with 3rd-party services.
## Tweaked

View File

@ -15,6 +15,7 @@
- 增加管理员专有的「STAFF」badge
- 在材质详情页中显示上传者的 badge
- 在错误页面增加指向 FAQ 页面的链接
- 第三方登录
## 调整

View File

@ -11,7 +11,10 @@
{% endif %}
<form></form>
<br>
<a href="{{ url('auth/register') }}">{{ trans('auth.register-link') }}</a>
{{ include('auth.oauth') }}
<div>
<a href="{{ url('auth/register') }}">{{ trans('auth.register-link') }}</a>
</div>
{% endblock %}
{% block before_foot %}

View File

@ -0,0 +1,9 @@
<div class="mb-3 text-center">
<div class="mb-1">- {{ trans('general.more') }} -</div>
{% for name, provider in providers %}
<a href="{{ url("/auth/login/#{name}") }}" class="btn btn-block bg-light border-secondary">
<img src="{{ url("/app/brand-icons/#{provider.icon}.svg") }}" width="20">
{{ provider.displayName }}
</a>
{% endfor %}
</div>

View File

@ -7,6 +7,7 @@
{{ trans('auth.register.message', {sitename: site_name}) }}
</p>
<form></form>
{{ include('auth.oauth') }}
{% endblock %}
{% block before_foot %}

View File

@ -27,6 +27,9 @@ Route::group(['prefix' => 'auth'], function () {
Route::get('/register', 'AuthController@register');
Route::get('/forgot', 'AuthController@forgot');
Route::get('/reset/{uid}', 'AuthController@reset')->name('auth.reset')->middleware('signed');
Route::get('/login/{driver}', 'AuthController@oauthLogin');
Route::get('/login/{driver}/callback', 'AuthController@oauthCallback');
});
Route::any('/logout', 'AuthController@logout');

View File

@ -13,6 +13,9 @@ if (Test-Path ./public/app) {
# Run webpack
yarn build
New-Item -ItemType Directory ./public/app/brand-icons
Copy-Item -Path ./node_modules/@fortawesome/fontawesome-free/svgs/brands/*.svg -Destination ./public/app/brand-icons
if ($Simple) {
exit
}

View File

@ -13,6 +13,8 @@ use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\Str;
use Laravel\Socialite\AbstractUser;
use Laravel\Socialite\Facades\Socialite;
class AuthControllerTest extends TestCase
{
@ -606,4 +608,80 @@ class AuthControllerTest extends TestCase
])->decodeResponseJson('token');
$this->assertTrue(is_string($token));
}
public function testOAuthLogin()
{
Socialite::shouldReceive('driver')
->with('github')
->once()
->andReturn(new class() {
public function redirect()
{
return redirect('/');
}
});
$this->get('/auth/login/github')->assertRedirect();
}
public function testOAuthCallback()
{
Event::fake();
Socialite::shouldReceive('driver')
->with('github')
->times(3)
->andReturn(
new class() {
public function user()
{
return new class() extends AbstractUser {
};
}
},
new class() {
public function user()
{
return new class() extends AbstractUser {
public $email = 'a@b.c';
public $nickname = 'abc';
};
}
},
new class() {
public function user()
{
return new class() extends AbstractUser {
public $email = 'a@b.c';
public $nickname = 'abc';
};
}
}
);
$this->get('/auth/login/github/callback')
->assertStatus(500)
->assertSee('Unsupported');
$this->get('/auth/login/github/callback')->assertRedirect('/user');
$this->assertDatabaseHas('users', [
'email' => 'a@b.c',
'nickname' => 'abc',
'score' => option('user_initial_score'),
'avatar' => 0,
'ip' => '127.0.0.1',
'permission' => User::NORMAL,
]);
Event::assertDispatched(Events\UserRegistered::class);
$this->assertAuthenticated();
auth()->logout();
$this->assertGuest();
$this->get('/auth/login/github/callback')->assertRedirect('/user');
Event::assertDispatched(Events\UserLoggedIn::class);
$this->assertAuthenticated();
}
}