Sonata User Bundle + Admin Bundle admin redirect after login - sonata-admin

I'm trying to make sonata work like this:
- if a regular user logs in he's redirected to "/"
- if an admin logs in he's redirected to "/admin/dashboard"
I tried to make it with firewalls that are in app/config/security.yml and here's what i come to:
# This firewall is used to handle the admin login area
# This part is handled by the Sonata User Bundle
admin:
pattern: /(.*)
context: user
form_login:
provider: fos_userbundle
login_path: /login
use_forward: false
check_path: /login_check
failure_path: null
default_target_path: /admin/dashboard
logout:
path: /admin/logout
target: /
anonymous: true
# This firewall is used to handle the public login area
# This part is handled by the FOS User Bundle
main:
pattern: .*
context: user
form_login:
provider: fos_userbundle
login_path: /login
use_forward: false
check_path: /login_check
failure_path: null
default_target_path: /
always_use_default_target_path: true
logout:
path: /logout
target: /
now every logged in user is redirected to /admin obviously throwing 'access denied' for non-admin users.
Is there any way to fix it in this yml file or shall i search for some different way of checking user roles?

One way to redirect user on basis of role you can implement your own authentication handler and check role of the user in onAuthenticationSuccess() function and redirect depending on the nature of user
namespace YourNamespace\YourBundle\Services;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
class AuthenticationHandler implements AuthenticationSuccessHandlerInterface {
protected $container;
public function __construct( $container ) {
$this->container = $container;
}
public function onAuthenticationSuccess( Request $request, TokenInterface $token ) {
$user = $token->getUser();
if($user->isGranted( 'ROLE_ADMIN' )){
$url = $this->container->get( 'router' )->generate( 'sonata_admin_dashboard' );
}else{
$url = $this->container->get( 'router' )->generate( 'your_welcome_route' );
}
return new RedirectResponse( $url );
}
}
define service for your authentication handler
services:
admin_success_handler:
class: YourNamespace\YourBundle\Services\AuthenticationHandler
arguments: [ '#service_container' ]
And in your firewall define success_handler
admin:
pattern: /(.*)
context: user
form_login:
provider: fos_userbundle
login_path: /login
use_forward: false
check_path: /login_check
failure_path: null
default_target_path: /admin/dashboard
success_handler: admin_success_handler
logout:
path: /admin/logout
target: /
anonymous: true

Related

How do i hookup a React button to authenticate with Devise Token Auth + OmniAuth

I'm having trouble connecting my Devise Token Auth with a token I get back from google in react.
I'm using this package for the button:
https://www.npmjs.com/package/react-google-login
This is the auth I'm trying to set up:
https://github.com/lynndylanhurley/devise_token_auth/blob/master/docs/config/omniauth.md
I'm getting a response from google with the react button but I have no idea how that information has to translate to go back to the devise auth.
Information online is severely lacking between these 2 technologies. What it comes down to is how to translate this ruby tag into react:
<%= link_to "Sign in with Google", user_google_oauth2_omniauth_authorize_path, method: :post %>
I know this is old but here are my 2 cents.
I have used this gem OmniAuth Google OAuth2. The information is pretty clear. In my project, I manage my token using JWT while still storing the access and refresh tokens from Google.
Backend
# config/initializers/devise.rb
config.omniauth :google_oauth2,
ENV['GOOGLE_CLIENT_ID'],
ENV['GOOGLE_CLIENT_SECRET'],
{ scope: "userinfo.email, userinfo.profile",
prompt: 'select_account',
image_aspect_ratio: 'square',
provider_ignores_state: true,
}
# controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def google_oauth2
#user = User.from_omniauth(request.env['omniauth.auth'])
if #user.persisted?
# My personal token
token = issue_token(#user)
render json: { success: true,
user: #user,
token: token,
google_token: #user.access_token,
message: "Logged in successfully." }
else
render json: { success: false,
error: #user.errors.full_messages.join("\n"),
message: 'Google sign in unsuccessful.' }
end
end
def failure
set_flash_message! :alert, :failure, kind: OmniAuth::Utils.camelize(failed_strategy.name), reason: failure_message
render json: { success: false, message: 'Google authentication failed.', reason: failure_message, kind: OmniAuth::Utils.camelize(failed_strategy.name) }
end
private
end
# In User.rb
def self.from_omniauth(auth)
user = User.where(email: auth.info.email).first
if user
user = User.update(id: user.id,
refresh_token: auth.credentials.refresh_token,
access_token: auth.credentials.token,
uid: auth.uid,
)
else
# Create a username from names, append incremental if username already exists
username ||= auth.info.first_name + auth.info.last_name
username = username.delete('^a-zA-Z0-9_').downcase
num = 1
until User.find_by(username: username).nil?
username = "#{username}#{num}"
num += 1
end
user = User.create(email: auth.info.email,
uid: auth.uid,
refresh_token: auth.credentials.refresh_token,
access_token: auth.credentials.token,
provider: auth.provider,
password: Devise.friendly_token[0, 20],
firstname: auth.info.first_name,
lastname: auth.info.last_name,
username: username,
)
end
user
end
# routes.rb
# User Routes: Devise
devise_for :users,
path_names: {
sign_in: 'login',
sign_out: 'logout',
# sign_up: 'register'
},
controllers: {
sessions: 'users/sessions',
registrations: 'users/registrations',
omniauth_callbacks: 'users/omniauth_callbacks'
}
Above routes translations
user_google_oauth2_omniauth_authorize_path GET|POST /api/users/auth/google_oauth2(.:format)
users/omniauth_callbacks#passthru
user_google_oauth2_omniauth_callback_path GET|POST /api/users/auth/google_oauth2/callback(.:format)
users/omniauth_callbacks#google_oauth2
Here is the front end
<!-- index.html -->
<head>
<script src="https://apis.google.com/js/platform.js?onload=init" async defer></script>
</head>
Do not worry about defining the gapi function, it is loaded script in the above
// RegisterContent.js
const RegisterContent = function RegisterContent() {
function handleResponse(response) {
// Save user to redux store and all the tokens to cookies
}
// callback
function signInCallback(authResult) {
if (authResult.code) {
const params = { code: authResult.code }
const path = "localhost:3000/api/users/auth/google_oauth2/callback";
// This just handdles posting with axios
postResource(path, params, handleResponse);
}
}
// This will prompt opening the google window and returns a callback upon success
const googleHandler = () => {
googleAuth.grantOfflineAccess().then(signInCallback);
};
useEffect(() => {
// Initialize the GoogleAuth object
gapi.load("auth2", function foo() {
const auth = gapi.auth2.init({
client_id: process.env.REACT_APP_GOOGLE_CLIENT_ID,
scope: "email profile",
});
setGoogleAuth(auth);
console.log("Init");
});
}, []);
return (
<Button onclick={googleHandler}>
Continue with Google
</Button>
);
}
A few resources to help Google Sign-In JavaScript client reference,
How to integrate Google API into your React app and that's it.
It's in the end just a post request to that endpoint but I have encountered the same problems as you are.
What you need to do is to create a form like this:
<form action="<%=user_google_oauth2_omniauth_authorize_path %>" method="post">
<input type="hidden" name="authenticity_token" value="XX">
<button type="submit">Connect Google</button>
</form>
My trials failed when I haven't passed the auth token or added a "skip_before_action :verify_autneticity_token" to the callback controller. You need to fill the correct authenticity token, then it works.
Authenticity token information can be added to your html page's head section, via <%= csrf_meta_tags %>. Then you will need to parse the dom for the meta fields to fill them correctly.

Angular deep link navigation with OIDC

I have an application https://app.example.com (home) and I have deep link working https://app.example.com/function/123 (direct_link) and navigating directly to direct_link works if the user is already authenticated.
We are using angular-oauth2-oidc and I can't find a way to initiate authentication and bring the user back to direct_link post authentication, it always returns to the home and I have paste the direct_link again in the address bar.
import { AuthConfig } from 'angular-oauth2-oidc';
export const authConfig: AuthConfig = {
// Url of the Identity Provider
issuer: 'https://cognito-idp.<region>.amazonaws.com/<id>',
// URL of the SPA to redirect the user to after login
redirectUri: window.location.origin,
// The SPA's id. The SPA is registerd with this id at the auth-server
clientId: '<id>',
// set the scope for the permissions the client should request
// The first three are defined by OIDC. The 4th is a usecase-specific one
scope: 'openid',
strictDiscoveryDocumentValidation: false,
responseType:'token',
oidc: true
}
export class AuthGuardService implements CanActivate{
constructor(private oauthService: OAuthService, private router: Router) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
if (this.oauthService.hasValidIdToken()) {
return true;
}
this.router.navigate(['home'], { queryParams: { returnUrl: state.url }});
return false;
}
}
export class HomeComponent implements OnInit {
returnUrl:string;
constructor(
private oauthService: OAuthService,
private router: Router) { }
login() {
this.oauthService.redirectUri = window.location.origin + this.returnUrl;
this.oauthService.initImplicitFlow();
}
logout() {
this.oauthService.logOut();
}
ngOnInit() {
}
}
We're using the angular-oauth2-oidc library with Azure AD B2C as well, and had a similar requirement.
Our deep linking requirements prevented us from using the redirectUri as the URL was dynamic (ie: product IDs included in the URL), and Azure AD B2C doesn't support wildcard redirectUris.
Our solution was to capture the current URL in session storage prior to invoking the oauthService's login flow, and then using that stored URL after the login is complete to redirect to the original URL, so for example:
export class AuthenticationService {
constructor(private storageService: SessionStorageService, private oauthService: OAuthService) { }
...
isLoggedIn(): boolean {
return this.oauthService.hasValidAccessToken();
}
...
login(): void {
this.oauthService.tryLoginImplicitFlow().then(success => {
if (!success) {
this.storageService.set('requestedUrl', location.pathname + location.search);
this.oauthService.initLoginFlow();
} else {
let requestedUrl = this.storageService.get('requestedUrl');
if (requestedUrl) {
sessionStorage.removeItem('requestedUrl');
location.replace( location.origin + requestedUrl);
}
}
This login method is part of our own auth service which mostly just delegates over to the OAuthService provided in the angular-oauth2-oidc package.
In our login method, we first attempt the tryLoginImplicitFlow() to see if the user has been authenticated.
If the tryLoginImplicitFlow() returns false, it means they aren't logged in, and we capture their current URL and shove it into session storage.
If it returns true, means they are authenticated, so we check to see if there is a stored URL, and if so, we redirect to it.
From a flow point of view, it works like this:
User attempts to access a deep link: /site/products/1234
App Component (not shown) checks the isLoggedIn() method of the auth service, and if not logged in, invokes the login() method
Login method tries the tryLoginImplicitFlow() (which does things like checking for a state hash in the URL), and it fails, so the method calls initLoginFlow()
User is redirected to some xxxx.b2clogin.com domain and logs in; B2C redirects the user to the root of our web app
App Component kicks in again and checks isLoggedIn(), which is still false, so calls the login() method
Login method tries the tryLoginImplicitFlow() (which picks up the fact that the user was just redirected from the B2C, and grabs the tokens) and it succeeds.
Login method checks session storage for the originally requested URL, sees it there, and redirects the user to that original page.
I know what you are thinking: "WOW! That's a whole lot of re-directs" ...and you are right - but it actually is surprisingly quick.

Custom login UI for IdentityServer 4

I am doing a project that is similar to IdentityServer4.Quickstart.UI, but separate the Login UI and IdentityServer. More specifically, the IdentityServer itself will be a pure WebApi and the Login UI can be any SPA built with Angualr or React or .... Currently, I am facing an issue Identity.Application was not authenticated. Failure message: Unprotect ticket failed.
The more detailed process is as: after clicking the login button in the SPA page, the following code
var result = await this._signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
will be run in the WebApi(IdentityServer) and the result is succeeded (user is logged in, which can be seen in UserSession in IdentityServer code).
Following the procedure in IdentityServer4.Quickstart.UI, the UI will be redirected to /connect/authorize/callback endpoint. However when I redirect the UI to /connect/authorize/callback, the user is no longer logged in. The error is Identity.Application was not authenticated. Failure message: Unprotect ticket failed. Please help to see what could be wrong. Some people said it is because the authentication cookie but I have no idea how to do it.
The detailed log is attached as follow
info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0]
User profile is available. Using 'C:\Users\Admin\AppData\Local\ASP.NET\DataProtection-Keys' as key repository and Windows DPAPI to encrypt keys at rest.
info: IdentityServer4.Startup[0]
You are using the in-memory version of the persisted grant store. This will store consent decisions, authorization codes, refresh and reference tokens in memory only. If you are using any of those features in production, you want to switch to a different store implementation.
dbug: IdentityServer4.Startup[0]
Custom IssuerUri set to http://localhost:5000
dbug: IdentityServer4.Startup[0]
Using Identity.Application as default ASP.NET Core scheme for authentication
dbug: IdentityServer4.Startup[0]
Using Identity.External as default ASP.NET Core scheme for sign-in
dbug: IdentityServer4.Startup[0]
Using Identity.External as default ASP.NET Core scheme for sign-out
dbug: IdentityServer4.Startup[0]
Using Identity.Application as default ASP.NET Core scheme for challenge
dbug: IdentityServer4.Startup[0]
Using Identity.Application as default ASP.NET Core scheme for forbid
Hosting environment: Development
Content root path: C:\Users\Admin\Downloads\Programming\IDS\Server
Now listening on: http://localhost:5000
Application started. Press Ctrl+C to shut down.
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 OPTIONS http://localhost:5000/Account/Login?returnUrl=http://localhost:5000/connect/authorize/callback?client_id=ce&redirect_uri=http%3A%2F%2Flocalhost%3A5050&response_type=id_token%20token&scope=openid%20profile%20CE&nonce=N0.90507026110735561540718014359&state=15407087903260.1454827167180146
info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4]
Policy execution successful.
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 20.8366ms 204
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 POST http://localhost:5000/Account/Login?returnUrl=http://localhost:5000/connect/authorize/callback?client_id=ce&redirect_uri=http%3A%2F%2Flocalhost%3A5050&response_type=id_token%20token&scope=openid%20profile%20CE&nonce=N0.90507026110735561540718014359&state=15407087903260.1454827167180146 application/json 61
info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4]
Policy execution successful.
dbug: IdentityServer4.Hosting.CorsPolicyProvider[0]
CORS request made for path: /Account/Login from origin: http://localhost:5001
dbug: IdentityServer4.Hosting.CorsPolicyProvider[0]
CorsPolicyService allowed origin: http://localhost:5001
info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4]
Policy execution successful.
dbug: IdentityServer4.Startup[0]
Login Url: http://localhost:5001/login
dbug: IdentityServer4.Startup[0]
Login Return Url Parameter: ReturnUrl
dbug: IdentityServer4.Startup[0]
Logout Url: /Account/Logout
dbug: IdentityServer4.Startup[0]
ConsentUrl Url: /consent
dbug: IdentityServer4.Startup[0]
Consent Return Url Parameter: returnUrl
dbug: IdentityServer4.Startup[0]
Error Url: /home/error
dbug: IdentityServer4.Startup[0]
Error Id Parameter: errorId
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
Route matched with {action = "Login", controller = "Account"}. Executing action Server.Controllers.AccountController.Login (Server)
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
Executing action method Server.Controllers.AccountController.Login (Server) with arguments (Server.Dto.Login) - Validation state: Valid
info: Microsoft.EntityFrameworkCore.Infrastructure[10403]
Entity Framework Core 2.1.4-rtm-31024 initialized 'DbContext' using provider 'Pomelo.EntityFrameworkCore.MySql' with options: None
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (88ms) [Parameters=[#__normalizedUserName_0='?' (Size = 256)], CommandType='Text', CommandTimeout='30']
SELECT `u`.`Id`, `u`.`AccessFailedCount`, `u`.`ConcurrencyStamp`, `u`.`Email`, `u`.`EmailConfirmed`, `u`.`LockoutEnabled`, `u`.`LockoutEnd`, `u`.`NormalizedEmail`, `u`.`NormalizedUserName`, `u`.`PasswordHash`, `u`.`PhoneNumber`, `u`.`PhoneNumberConfirmed`, `u`.`SecurityStamp`, `u`.`TwoFactorEnabled`, `u`.`UserName`
FROM `AspNetUsers` AS `u`
WHERE `u`.`NormalizedUserName` = #__normalizedUserName_0
LIMIT 1
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (6ms) [Parameters=[#__user_Id_0='?' (Size = 255)], CommandType='Text', CommandTimeout='30']
SELECT `uc`.`Id`, `uc`.`ClaimType`, `uc`.`ClaimValue`, `uc`.`UserId`
FROM `AspNetUserClaims` AS `uc`
WHERE `uc`.`UserId` = #__user_Id_0
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (30ms) [Parameters=[#__userId_0='?' (Size = 255)], CommandType='Text', CommandTimeout='30']
SELECT `role`.`Name`
FROM `AspNetUserRoles` AS `userRole`
INNER JOIN `AspNetRoles` AS `role` ON `userRole`.`RoleId` = `role`.`Id`
WHERE `userRole`.`UserId` = #__userId_0
dbug: IdentityServer4.Hosting.IdentityServerAuthenticationService[0]
Augmenting SignInContext
dbug: IdentityServer4.Hosting.IdentityServerAuthenticationService[0]
Adding idp claim with value: local
dbug: IdentityServer4.Hosting.IdentityServerAuthenticationService[0]
Adding amr claim with value: pwd
dbug: IdentityServer4.Hosting.IdentityServerAuthenticationService[0]
Adding auth_time claim with value: 1540720363
info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[10]
AuthenticationScheme: Identity.Application signed in.
info: Server.Controllers.AccountController[0]
User logged in.
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
Executed action method Server.Controllers.AccountController.Login (Server), returned result Microsoft.AspNetCore.Mvc.OkResult in 25685.0508ms.
info: Microsoft.AspNetCore.Mvc.StatusCodeResult[1]
Executing HttpStatusCodeResult, setting HTTP status code 200
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
Executed action Server.Controllers.AccountController.Login (Server) in 26016.4477ms
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 26239.7052ms 200
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 GET http://localhost:5000/connect/authorize/callback?client_id=ce&redirect_uri=http%3A%2F%2Flocalhost%3A5050&response_type=id_token%20token&scope=openid%20profile%20CE&nonce=N0.90507026110735561540718014359&state=15407087903260.1454827167180146
info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[7]
Identity.Application was not authenticated. Failure message: Unprotect ticket failed
info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[7]
Identity.Application was not authenticated. Failure message: Unprotect ticket failed
dbug: IdentityServer4.Hosting.EndpointRouter[0]
Request path /connect/authorize/callback matched to endpoint type Authorize
dbug: IdentityServer4.Hosting.EndpointRouter[0]
Endpoint enabled: Authorize, successfully created handler: IdentityServer4.Endpoints.AuthorizeCallbackEndpoint
info: IdentityServer4.Hosting.IdentityServerMiddleware[0]
Invoking IdentityServer endpoint: IdentityServer4.Endpoints.AuthorizeCallbackEndpoint for /connect/authorize/callback
dbug: IdentityServer4.Endpoints.AuthorizeCallbackEndpoint[0]
Start authorize callback request
info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[7]
Identity.Application was not authenticated. Failure message: Unprotect ticket failed
dbug: IdentityServer4.Endpoints.AuthorizeCallbackEndpoint[0]
No user present in authorize request
dbug: IdentityServer4.Validation.AuthorizeRequestValidator[0]
Start authorize request protocol validation
dbug: IdentityServer4.Stores.ValidatingClientStore[0]
client configuration validation for client ce succeeded.
dbug: IdentityServer4.Validation.AuthorizeRequestValidator[0]
Calling into custom validator: IdentityServer4.Validation.DefaultCustomAuthorizeRequestValidator
info: IdentityServer4.Endpoints.AuthorizeCallbackEndpoint[0]
ValidatedAuthorizeRequest
{
"ClientId": "ce",
"ClientName": "ce",
"RedirectUri": "http://localhost:5050",
"AllowedRedirectUris": [
"http://localhost:5050"
],
"SubjectId": "anonymous",
"ResponseType": "id_token token",
"ResponseMode": "fragment",
"GrantType": "implicit",
"RequestedScopes": "openid profile CE",
"State": "15407087903260.1454827167180146",
"Nonce": "N0.90507026110735561540718014359",
"Raw": {
"client_id": "ce",
"redirect_uri": "http://localhost:5050",
"response_type": "id_token token",
"scope": "openid profile CE",
"nonce": "N0.90507026110735561540718014359",
"state": "15407087903260.1454827167180146"
}
}
info: IdentityServer4.ResponseHandling.AuthorizeInteractionResponseGenerator[0]
Showing login: User is not authenticated
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 970.0804ms 302
Here is the key part of StartUp.cs
services.AddDbContext<DbContext>(options =>
options.UseMySql("server=10.1.1.228;database=IdentityService;user=admin;password=admin"));
services.AddCors(options =>
{
options.AddPolicy("default", policy =>
{
policy.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
});
});
services.AddIdentity<SsoUser, IdentityRole>(options =>
{
options.Tokens.ChangePhoneNumberTokenProvider = "Phone";
options.Password.RequiredLength = 8;
options.Password.RequiredUniqueChars = 0;
options.Password.RequireDigit = false;
options.Password.RequireLowercase = false;
options.Password.RequireUppercase = false;
options.Password.RequireNonAlphanumeric = false;
})
.AddEntityFrameworkStores<DbContext>()
.AddDefaultTokenProviders();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddIdentityServer(x =>
{
x.IssuerUri = "http://localhost:5000";
x.UserInteraction.LoginUrl = "http://localhost:5001/login";
x.Cors.CorsPaths.Add(new PathString("/Account/Login"));
}
).AddDeveloperSigningCredential()
.AddAspNetIdentity<SsoUser>()
.AddInMemoryIdentityResources(InMemoryConfiguration.GetIdentityResources())
.AddInMemoryApiResources(InMemoryConfiguration.ApiResources())
.AddInMemoryClients(InMemoryConfiguration.Clients())
.AddTestUsers(InMemoryConfiguration.Users().ToList())
.AddCorsPolicyService<CorsPolicyService>();
services.AddAuthentication().AddCookie();
The main reason is that Angular did not send a cookie to IdentityServer.
In the UI, add withCredentials: true to the httpClient option.
And in IdentityServer's Startup.cs, add:
services.AddCors(options =>
{
options.AddPolicy("default", policy =>
{
policy.WithOrigins("UI Domain")
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
(answer moved out of the question to a CW post)

Bad Credentials - ajax login - Symfony2 / AngularJS / FosUserBundle

FosUserBundle works fine with the normal web/app_dev.php/login. I've implemented the AuthenticationHandler this way: here
My Angular sends a JSON request:
_csrf_token: "uSRZfxMycFCLKbxD_d9kgp-7WhM5ECrFyP6vtHDOle0"
_password: "welkom"
_submit: "security.login.submit"
_username: "erwin#eberhard.com"
The same data as the 'normal' login form which the FOSUserBundle provides. The CSRF-token is taken from the cookie:
_csrf_token = $cookies['XSRF-TOKEN'].
It's the same token that lives serverside. It has been checked by var_dump().
The cookie is set in the controller:
$engine = $this->container->get('templating');
$content = $engine->render('ErwinEventManagerBundle:Default:index.html.twig', array('name' => "noname"));
$csrf = $this->get('form.csrf_provider')->generateCsrfToken('authenticate');
$response = new Response($content);
$cookie = new Cookie("XSRF-TOKEN", $csrf, time() + 3600 * 24 , '/', null, false, false);
$response->headers->setCookie($cookie);
return $this->render('ErwinEventManagerBundle:Default:index.html.twig', array('name' => "noname"));
The response is:
{"success":false,"message":"Invalid CSRF token."}
Request Headers:
Accept:application/json
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8,nl;q=0.6
Authorization:null
Connection:keep-alive
Content-Length:153
Content-Type:application/json;charset=UTF-8
Cookie:XSRF-TOKEN=uSRZfxMycFCLKbxD_d9kgp-7WhM5ECrFyP6vtHDOle0;
PHPSESSID=o9dc41tglf9dkl07fv7pdjp545
Host:erwin.com
Origin:http://erwin.com
Referer:http://erwin.com/Symfony/web/app_dev.php/
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/35.0.1916.153 Safari/537.36
X-Requested-With:XMLHttpRequest
X-XSRF-TOKEN:uSRZfxMycFCLKbxD_d9kgp-7WhM5ECrFyP6vtHDOle0
My config.yml contains:
framework:
# #esi: ~
# #translator: { fallback: "%locale%" }
secret: "%secret%"
router:
resource: "%kernel.root_dir%/config/routing.yml"
strict_requirements: ~
form: ~
csrf_protection: ~
validation: { enable_annotations: true }
templating:
engines: ['twig']
#assets_version: SomeVersionScheme
packages:
jquery:
base_urls:
# http: [http://my.cdn.url]
ssl: [ https://code.jquery.com ]
http: [ http://code.jquery.com ]
angular:
base_urls:
http: [ http://ajax.googleapis.com/ajax/libs/angularjs/1.2.16 ]
http: [ https://ajax.googleapis.com/ajax/libs/angularjs/1.2.16 ]
default_locale: "%locale%"
trusted_hosts: ~
trusted_proxies: ~
session:
# handler_id set to null will use default session handler from php.ini
handler_id: ~
fragments: ~
http_method_override: true
My security.yml contains:
encoders:
FOS\UserBundle\Model\UserInterface: sha512
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
providers:
fos_userbundle:
# id: fos_user.user_provider.username
id: fos_user.user_provider.username_email
firewalls:
main:
pattern: ^/
form_login:
# check_path: security_check_route
success_handler: erwin_security.authentication_handler
failure_handler: erwin_security.authentication_handler
csrf_provider: form.csrf_provider
logout: true
anonymous: true
EDIT
After I removed CSRF I got Bad Credentials feedback. Ik add the form twice in my page. The 'normal' version works fine, the ajax-version not.
I hope this will help.
I had the same problem but with LexikJWTAuthenticationBundle
The 'normal' form works fine, the ajax-version not. (the form is posted to the check_path )
What I did :
Solution 1 :
I installed symfony-json-request-transformer A Symfony 2 event listener for decoding JSON encoded request content.
Solution 2 : (Bad, don't do it, this is for debugging ! -) I modified this symfony listener : Symfony\Component\Security\Http\Firewall\UsernamePasswordFormAuthenticationListener
Like this :
protected function attemptAuthentication(Request $request) {
// ...
if ($request->isXmlHttpRequest()) {
$request->request->replace(json_decode($request->getContent(), true));
}
// ...
}
In AngularJS add : $http.post('path', credentials, {headers : {'X-Requested-with' : 'XMLHttpRequest'}}...

passport-ldapauth is not working properly

I have setup LDAP server in my local system by this link How To Install and Configure a Basic LDAP Server . I mange to get LDAP server run and able to add group, user, etc through phpldapadmin. I have added a user as shown in this image
To confirm the user in LDAP server I used below command
$ ldapsearch -x -W -D 'cn=thiru niren,cn=users,ou=group,dc=test,dc=com' -b "" -s base -H ldap://192.168.2.4
It asked me for password then I entered password and I get result
Enter LDAP Password:
# extended LDIF
#
# LDAPv3
# base <> with scope baseObject
# filter: (objectclass=*)
# requesting: ALL
#
#
dn:
objectClass: top
objectClass: OpenLDAProotDSE
# search result
search: 2
result: 0 Success
# numResponses: 2
# numEntries: 1
Then I tried to use passport-ldapauth module. Code I have written for this is
var LdapStrategy = require('passport-ldap').Strategy,
passport = require('passport');
passport.use(new LdapStrategy({server: {
url: 'ldap://192.168.2.4',
adminDn:'cn=thiru niren,cn=users,ou=group,dc=test,dc=com',
adminPassword: 'infra'
}},
function(user, done) {
console.log("user authenticated or not");
return done(null, user);
}
));
console.log("check ldap");
After I execute this code, I see only check ldap and a keep on blinking cursor in terminal. Then I put up console.log("user authenticated or not"); to verify whether LdapStrategy is call callback function or not but I don't see user authenticated or not in console but check ldap. What would be the problem? why passport-ldapaut is don't get any response from ldap server?
I'm using ubuntu 14.04 LTS.
node --version is v0.10.28.
Update:
I tried with ldapauth module as well but no luck I get same result, check my modified code of above mentioned website
var connect = require('connect');
var LdapAuth = require('ldapauth');
// Config from a .json or .ini file or whatever.
var config = {
ldap: {
url: "ldap://192.168.2.4:389",
adminDn: "cn=thiru niren,cn=users,ou=group,dc=test,dc=com",
adminPassword: "infra",
searchBase: "ou=group,dc=test,dc=com",
searchFilter: "(uid=tniren)"
}
};
var ldap = new LdapAuth({
url: config.ldap.url,
adminDn: config.ldap.adminDn,
adminPassword: config.ldap.adminPassword,
searchBase: config.ldap.searchBase,
searchFilter: config.ldap.searchFilter,
//log4js: require('log4js'),
cache: true
});
//console.log("we here",ldap);
var basicAuthMiddleware = connect.basicAuth(function (username, password, callback) {
console.log("we");
ldap.authenticate(username, password, function (err, user) {
console.log("we are");
if (err) {
console.log("LDAP auth error: %s", err);
}
callback(err, user)
});
});
Here also I don't get any response from ldap server, is there any problem with server?

Resources