Closed bzzzm closed 10 years ago
Hi @bzzzm,
I spot that you use sha1 to generate your digest on the client side, but expect (see firewalls-section in security.yml) a sha512 with 1 iteration on the server side.
Make use of the encoding used by FOSUserBundle on the server side (security.encoders + wsse.encoder settings) and use the same to generate the digest on the client side as well...
Hope this helps!
Kind regards, David
Hey @djoos,
Thanks for your comment. I think I solved the encoding part by generating the digest using security.encoder_factory
, but I still have the same problem. Here is the Handler now:
public function __construct(SecurityContext $security, EncoderFactory $factory) {
$this->security = $security;
$this->factory = $factory;
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token) {
$user = $this->security->getToken()->getUser();
$encoder = $this->factory->getEncoder($user);
$created = date('c');
$nonce = substr(md5(uniqid('nonce_', true)), 0, 16);
$nonceHigh = base64_encode($nonce);
$passwordDigest = base64_encode(
$encoder->encodePassword(
$nonceHigh .
$created .
$user->getPassword(),
$user->getSalt()));
$header = "UsernameToken Username=\"{$user->getUsername()}\", PasswordDigest=\"{$passwordDigest}\", Nonce=\"{$nonceHigh}\", Created=\"{$created}\"";
return new Response($header);
}
}
I also changed security.firewalls.wsse_secured.wsse.interations
from 1 to 5000:
// print_r($this->factory->getEncoder($user));
Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder Object
(
[algorithm:Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder:private] => sha512
[encodeHashAsBase64:Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder:private] => 1
[iterations:Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder:private] => 5000
)
I also cleared cache after every change i made in the code. Thank you.
Hi @bzzzm,
I'm not 100% sure what you're trying to achieve returning the WSSE header as a response on authentication success to be honest. It might be worth isolating the WSSE side of things by creating a simple controller/command that posts (via curl) to your WSSE secured API...
Let me know how it goes!
Kind regards, David
Hello @djoos,
It's just while testing, I wont have such handler after I solve this issue. I'm doing that for faster copy/paste. The problem is not during login_check, but when I try to access some other controller in /back with the wsse header (the one returned by the handle of one generated on teria.com); that's when I get Previously used nonce detected.
Thank you!
Hi @bzzzm,
you can't reuse nonces within the set lifetime: you'll need a new one every call... You coild set the lifetime to 0, but I would strongly advise against that.
Hope this helps! David
Hi @djoos, I solved it after all. In the Handler I had this:
$passwordDigest = base64_encode(
$encoder->encodePassword(
$nonceHigh .
$created .
$user->getPassword(),
$user->getSalt()));
and should have been this (without base64):
$passwordDigest = $encoder->encodePassword(
$nonce .
$created .
$user->getPassword(),
$user->getSalt());
This is because the encoder will automatically do a base64_encode.
[encodeHashAsBase64:Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder:private] => 1
Also, the nonce should not be base64 encrypted in the passwordDigest.
Thank you for you help!
You're welcome, great to hear you're up and running!
Kind regards, David
Hi @djoos
I want to linked WSSE Auth with my Rest API (sf2 with fosrestbundle, fosuserbundle & nelmio) I generate my token via Nelmio doc and i have config your bundle but every time i try my log in _profiler write
this:INFO - Matched route "get_user_role". Context: {"route_parameters":{"_controller":"AppBundle\Controller\UserRoleRestController::getRoleAction","_format":"json","slug":"cpasche","_route":"get_user_role"},"request_uri":"http://bend.example.dev.com/web/app_dev.php/api/v1/users/username/role.json"} WARNING - WSSE authentication failed.
My TokenRestController ` class TokenRestController extends FOSRestController {
/**
* Create a Token from the submitted data.<br/>
*
* @ApiDoc(
* resource = true,
* description = "Creates a new token from the submitted data.",
* statusCodes = {
* 200 = "Returned when successful",
* 400 = "Returned when the form has errors"
* }
* )
*
* @param ParamFetcher $paramFetcher Paramfetcher
*
* @RequestParam(name="username", nullable=false, strict=true, description="username.")
* @RequestParam(name="password", nullable=false, strict=true, description="password.")
* @RequestParam(name="salt", nullable=false, strict=true, description="salt.")
*
* @return View
*/
public function postTokenAction(ParamFetcher $paramFetcher)
{
$view = View::create();
$userManager = $this->get('fos_user.user_manager');
$user = $userManager->findUserByUsername($paramFetcher->get('username'));
if (!$user instanceof User) {
$view->setStatusCode(404)->setData("Data received succesfully but with errors.");
return $view;
}
$factory = $this->get('security.encoder_factory');
$encoder = $factory->getEncoder($user);
$password = $encoder->encodePassword($paramFetcher->get('password'), $paramFetcher->get('salt'));
$header = $this->generateToken($paramFetcher->get('username'), $password);
$data = array('X-WSSE' => $header);
$view->setHeader("Authorization", 'WSSE profile="UsernameToken"');
$view->setHeader("X-WSSE", $header);
$view->setStatusCode(200)->setData($data);
return $view;
}
/**
* Generate token for username given
*
* @param string $username username
* @param string $password password with salt included
* @return string
*/
private function generateToken($username, $password)
{
$nonce = md5(rand());
$created = gmdate(DATE_ISO8601);
$digest = base64_encode(sha1($nonce.$created.$password, true));
$b64nonce = base64_encode($nonce);
$token = 'UsernameToken Username="'.$username.'", PasswordDigest="'.$digest.'", Nonce="'.$b64nonce.'", Created="'.$created.'"';
/* $created = date('c');
$nonce = substr(md5(uniqid('nonce_', true)), 0, 16);
$nonceSixtyFour = base64_encode($nonce);
$passwordDigest = base64_encode(sha1($nonce . $created . $password, true));
$token = sprintf(
'UsernameToken Username="%s", PasswordDigest="%s", Nonce="%s", Created="%s"',
$username,
$passwordDigest,
$nonceSixtyFour,
$created
); */
return $token;
}
} `
AND my config security.yml
`security: encoders: FOS\UserBundle\Model\UserInterface: algorithm: sha512 encode_as_base64: true iterations: 1
role_hierarchy:
ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_API, ROLE_ALLOWED_TO_SWITCH]
providers:
fos_userbundle:
id: fos_user.user_provider.username_email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
wsse_secured:
pattern: ^/api/.*
context: user
stateless: true
wsse:
realm: "Secured with WSSE" #identifies the set of resources to which the authentication information will apply (WWW-Authenticate)
profile: "UsernameToken" #WSSE profile (WWW-Authenticate)
provider: fos_userbundle
lifetime: 600
encoder:
algorithm: sha512
encodeHashAsBase64: true
iterations: 1
anonymous: true
access_control:
- { path: ^/.*, role: IS_AUTHENTICATED_ANONYMOUSLY }
`
I had after when i try this with nelmio doc headers
Authorization:"WSSE profile="UsernameToken" X-WSSE: UsernameToken Username ....
Can you help me ? Thx a lot !
Date: Mon, 19 Dec 2016 20:41:45 GMT X-Debug-Token-Link: /web/app_dev.php/_profiler/abca90 WWW-Authenticate: WSSE realm="Secured with WSSE", profile="UsernameToken" Server: Apache/2.4.9 (Win64) PHP/5.5.12 X-Powered-By: PHP/5.5.12 Allow: GET Content-Type: application/json Access-Control-Allow-Origin: * Cache-Control: no-cache Connection: Keep-Alive Vary: Authorization Content-Length: 0 X-Debug-Token: abca90 Keep-Alive: timeout=5, max=100
Hello,
I have a problem in getting a wsse authentication running using your bundle (thx!) and fosuserbundle. I'm able to login (i can see in the log that the user is granted access.. also visible in the profiler security tab, user last_login gets updated in db etc etc).
I also created a Handler that listens for
onAuthenticationSuccess
and returns the contents ofX-WSSE
header (i can't remember where i saw a similar script):After that, I take the header, paste it in the Chrome Rest Console... but I get a 401. Here is the log of such request:
I also tried generating the header using teria.com generator (got the salt+pass for db), but the problem persists.
Here is my security.yml
Does anyone have any idea what I'm missing?
Thank you