This repository has been archived by the owner on Jan 21, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 46
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[#83] Return a 401 for invalid OAuth2 credentials
`OAuth2\Server` returns boolean false for each of: - no token present - invalid and/or expired token present - token invalid for current scope The first case, is expected; it's what will happen in a public API when no credentials are provided, and should result in marshaling a `GuestIdentity`. The second two cases, however, should result in returning the appropriate status code and headers. For this to happen, we must introspect the response composed in the server: - First, to see if we have a 401 or 403 status, and - second, to see if an error has been reported to the response. If both conditions occur, we merge the `OAuth2\Response` status and headers to the MVC response and return it immediately. Otherwise, we only merge any headers present (typically, the `WWW-Authenticate` header, which will be a prompt for clients to know that authentication is possible), and return a `GuestIdentity`.
- Loading branch information
1 parent
4a3fc36
commit 101feec
Showing
4 changed files
with
281 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
<?php | ||
/** | ||
* @license http://opensource.org/licenses/BSD-3-Clause BSD-3-Clause | ||
* @copyright Copyright (c) 2014 Zend Technologies USA Inc. (http://www.zend.com) | ||
*/ | ||
|
||
namespace ZFTest\MvcAuth\Authentication; | ||
|
||
use ArrayIterator; | ||
use OAuth2\Request as OAuth2Request; | ||
use PHPUnit_Framework_TestCase as TestCase; | ||
use Zend\Http\PhpEnvironment\Request as HttpRequest; | ||
use Zend\Http\Response as HttpResponse; | ||
use ZF\MvcAuth\Authentication\OAuth2Adapter; | ||
|
||
class OAuth2AdapterTest extends TestCase | ||
{ | ||
public function setUp() | ||
{ | ||
$this->oauthServer = $this->getMock('OAuth2\Server'); | ||
$this->adapter = new OAuth2Adapter($this->oauthServer); | ||
} | ||
|
||
/** | ||
* @group 83 | ||
*/ | ||
public function testReturns401ResponseWhenErrorOccursDuringValidation() | ||
{ | ||
$oauth2Response = $this->getMockBuilder('OAuth2\Response') | ||
->disableOriginalConstructor() | ||
->getMock(); | ||
$oauth2Response | ||
->expects($this->once()) | ||
->method('getStatusCode') | ||
->willReturn(401); | ||
$oauth2Response | ||
->expects($this->once()) | ||
->method('getParameter') | ||
->with($this->equalTo('error')) | ||
->willReturn('invalid'); | ||
$oauth2Response | ||
->expects($this->once()) | ||
->method('getHttpHeaders') | ||
->willReturn(array()); | ||
|
||
$this->oauthServer | ||
->expects($this->once()) | ||
->method('verifyResourceRequest') | ||
->with($this->callback(function ($subject) { | ||
return ($subject instanceof OAuth2Request); | ||
})) | ||
->willReturn(false); | ||
|
||
$this->oauthServer | ||
->expects($this->once()) | ||
->method('getResponse') | ||
->willReturn($oauth2Response); | ||
|
||
$mvcAuthEvent = $this->getMockBuilder('ZF\MvcAuth\MvcAuthEvent') | ||
->disableOriginalConstructor() | ||
->getMock(); | ||
|
||
$result = $this->adapter->authenticate(new HttpRequest, new HttpResponse, $mvcAuthEvent); | ||
$this->assertInstanceOf('Zend\Http\Response', $result); | ||
$this->assertEquals(401, $result->getStatusCode()); | ||
} | ||
|
||
/** | ||
* @group 83 | ||
*/ | ||
public function testReturns403ResponseWhenInvalidScopeDetected() | ||
{ | ||
$oauth2Response = $this->getMockBuilder('OAuth2\Response') | ||
->disableOriginalConstructor() | ||
->getMock(); | ||
$oauth2Response | ||
->expects($this->once()) | ||
->method('getStatusCode') | ||
->willReturn(403); | ||
$oauth2Response | ||
->expects($this->once()) | ||
->method('getParameter') | ||
->with($this->equalTo('error')) | ||
->willReturn('invalid'); | ||
$oauth2Response | ||
->expects($this->once()) | ||
->method('getHttpHeaders') | ||
->willReturn(array()); | ||
|
||
$this->oauthServer | ||
->expects($this->once()) | ||
->method('verifyResourceRequest') | ||
->with($this->callback(function ($subject) { | ||
return ($subject instanceof OAuth2Request); | ||
})) | ||
->willReturn(false); | ||
|
||
$this->oauthServer | ||
->expects($this->once()) | ||
->method('getResponse') | ||
->willReturn($oauth2Response); | ||
|
||
$mvcAuthEvent = $this->getMockBuilder('ZF\MvcAuth\MvcAuthEvent') | ||
->disableOriginalConstructor() | ||
->getMock(); | ||
|
||
$result = $this->adapter->authenticate(new HttpRequest, new HttpResponse, $mvcAuthEvent); | ||
$this->assertInstanceOf('Zend\Http\Response', $result); | ||
$this->assertEquals(403, $result->getStatusCode()); | ||
} | ||
|
||
/** | ||
* @group 83 | ||
*/ | ||
public function testReturnsGuestIdentityIfOAuth2ResponseIsNotAnError() | ||
{ | ||
$oauth2Response = $this->getMockBuilder('OAuth2\Response') | ||
->disableOriginalConstructor() | ||
->getMock(); | ||
$oauth2Response | ||
->expects($this->once()) | ||
->method('getStatusCode') | ||
->willReturn(200); | ||
$oauth2Response | ||
->expects($this->once()) | ||
->method('getHttpHeaders') | ||
->willReturn(array()); | ||
|
||
$this->oauthServer | ||
->expects($this->once()) | ||
->method('verifyResourceRequest') | ||
->with($this->callback(function ($subject) { | ||
return ($subject instanceof OAuth2Request); | ||
})) | ||
->willReturn(false); | ||
|
||
$this->oauthServer | ||
->expects($this->once()) | ||
->method('getResponse') | ||
->willReturn($oauth2Response); | ||
|
||
$mvcAuthEvent = $this->getMockBuilder('ZF\MvcAuth\MvcAuthEvent') | ||
->disableOriginalConstructor() | ||
->getMock(); | ||
|
||
$result = $this->adapter->authenticate(new HttpRequest, new HttpResponse, $mvcAuthEvent); | ||
$this->assertInstanceOf('ZF\MvcAuth\Identity\GuestIdentity', $result); | ||
} | ||
|
||
/** | ||
* @group 83 | ||
*/ | ||
public function testErrorResponseIncludesOAuth2ResponseHeaders() | ||
{ | ||
$expectedHeaders = array( | ||
'WWW-Authenticate' => 'Bearer realm="example.com", ' | ||
. 'scope="user", ' | ||
. 'error="unauthorized", ' | ||
. 'error_description="User has insufficient privileges"', | ||
); | ||
$oauth2Response = $this->getMockBuilder('OAuth2\Response') | ||
->disableOriginalConstructor() | ||
->getMock(); | ||
$oauth2Response | ||
->expects($this->once()) | ||
->method('getStatusCode') | ||
->willReturn(401); | ||
$oauth2Response | ||
->expects($this->once()) | ||
->method('getParameter') | ||
->with($this->equalTo('error')) | ||
->willReturn('invalid'); | ||
$oauth2Response | ||
->expects($this->once()) | ||
->method('getHttpHeaders') | ||
->willReturn($expectedHeaders); | ||
|
||
$this->oauthServer | ||
->expects($this->once()) | ||
->method('verifyResourceRequest') | ||
->with($this->callback(function ($subject) { | ||
return ($subject instanceof OAuth2Request); | ||
})) | ||
->willReturn(false); | ||
|
||
$this->oauthServer | ||
->expects($this->once()) | ||
->method('getResponse') | ||
->willReturn($oauth2Response); | ||
|
||
$mvcAuthEvent = $this->getMockBuilder('ZF\MvcAuth\MvcAuthEvent') | ||
->disableOriginalConstructor() | ||
->getMock(); | ||
|
||
$result = $this->adapter->authenticate(new HttpRequest, new HttpResponse, $mvcAuthEvent); | ||
$this->assertInstanceOf('Zend\Http\Response', $result); | ||
|
||
$headers = $result->getHeaders(); | ||
foreach ($expectedHeaders as $name => $value) { | ||
$this->assertTrue($headers->has($name)); | ||
$header = $headers->get($name); | ||
if ($header instanceof ArrayIterator) { | ||
$found = false; | ||
foreach ($header as $instance) { | ||
if ($instance->getFieldValue() == $value) { | ||
$found = true; | ||
break; | ||
} | ||
} | ||
$this->assertTrue($found, 'Expected header value not found'); | ||
continue; | ||
} | ||
|
||
$this->assertEquals($value, $header->getFieldValue()); | ||
} | ||
} | ||
} |