Installation
composer require chrisyue/dark-portal-bundle:dev-master
Add the bundle to your AppKernel.php (Symfony 2.x) or config/bundles.php (Symfony 3.x+):
new Chrisyue\DarkPortalBundle\ChrisyueDarkPortalBundle(),
Configure Security
Update config/packages/security.yaml (or app/config/security.yml for Symfony 2.x):
providers:
oauth:
id: chrisyue_dark_portal.security.user.provider
firewalls:
secured_area:
pattern: ^/
oauth_code:
appid: "%env(OAUTH_APP_ID)%"
secret: "%env(OAUTH_SECRET)%"
scope: "snsapi_login"
code_endpoint: "http://oauth-code.xxx.com/get-code.php"
provider: oauth
Deploy the Code Endpoint
Download get-code.php and place it on your dedicated oauth-code.xxx.com server.
Configure allowed hosts in the script:
$hosts = [
'wechat.xxx.com',
'weixin.xxx.com',
];
First Use Case Redirect users to the OAuth provider (e.g., WeChat) via:
return $this->get('security.token_storage')->getToken()->getUser()->getAuthenticator()->getLoginUrl();
The bundle handles the code exchange transparently, allowing multi-host redirects.
Multi-Host OAuth Flow
oauth-code.xxx.com as a single entry point for all OAuth code requests.get-code.php to validate redirects.code, the bundle exchanges it for an access_token without host restrictions.Symfony Security Integration
UserProvider to fetch user data after token acquisition.// src/Security/OAuthUserProvider.php
use Chrisyue\DarkPortalBundle\Security\User\OAuthUserProvider as BaseProvider;
class OAuthUserProvider extends BaseProvider {
public function loadUserByOAuthToken($token) {
// Custom logic to fetch user from OAuth API
}
}
Dynamic Endpoint Configuration
code_endpoint in security.yaml:
oauth_code:
code_endpoint: "%env(OAUTH_CODE_ENDPOINT)%"
get-code.php to a subdomain or separate server for isolation.CSRF Protection
get-code.php script includes CSRF checks. Ensure your frontend includes a hidden CSRF token in the OAuth redirect.State Parameter Handling
state parameter (e.g., ?state=wechat) to distinguish between multiple OAuth providers or flows.Host Validation Strictness
get-code.php script strictly checks the Referer header against $hosts. If a request comes from an unallowed domain, it fails silently (no error message by default).get-code.php:
if (!in_array($_SERVER['HTTP_REFERER'], $hosts)) {
error_log("Invalid referer: " . $_SERVER['HTTP_REFERER']);
header("Location: /error");
exit;
}
Token Exchange Failures
code expires before exchange, the OAuth provider may return an error. Ensure get-code.php is highly available and the code is exchanged promptly.code exchange asynchronously if latency is a concern.CORS Issues
oauth-code.xxx.com are on different domains, ensure CORS headers are set in get-code.php:
header("Access-Control-Allow-Origin: https://wechat.xxx.com");
State Parameter Mismatch
state parameter to prevent CSRF attacks. The bundle does not handle this by default.// In get-code.php
if ($_GET['state'] !== $_SESSION['expected_state']) {
die("Invalid state");
}
Environment-Specific Config
code_endpoint in security.yaml. Use %kernel.environment% or environment variables:
code_endpoint: "%env(OAUTH_CODE_ENDPOINT_%kernel.environment%)%"
Enable Verbose Logging
Add this to config/packages/monolog.yaml:
handlers:
oauth:
type: stream
path: "%kernel.logs_dir%/oauth.log"
level: debug
channels: ["oauth"]
Then log critical steps in OAuthUserProvider.
Test with Postman/cURL Manually trigger the OAuth flow to debug:
curl -v "https://oauth-code.xxx.com/get-code.php?code=AUTH_CODE&state=test"
Check get-code.php Permissions
Ensure the script has:
session.save_path if using PHP sessions.Custom User Provider
Extend BaseOAuthUserProvider to add logic like:
Multi-Provider Support
Modify get-code.php to accept a provider parameter (e.g., ?provider=wechat) and route to different OAuth endpoints.
Token Storage
Override the default token storage to persist access_token and refresh_token in your database:
// src/Security/OAuthTokenStorage.php
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class OAuthTokenStorage implements TokenStorageInterface {
public function setToken(TokenInterface $token) {
// Save token to DB
parent::setToken($token);
}
}
Webhook for Token Refresh
Use the bundle’s refresh_token endpoint (if supported by the provider) to silently refresh tokens in the background.
How can I help you explore Laravel packages today?