Browse Source

Token verification and revocation

microsub
J. King 5 years ago
parent
commit
cbd382d768
  1. 67
      lib/REST/Microsub/Auth.php

67
lib/REST/Microsub/Auth.php

@ -196,7 +196,7 @@ class Auth extends \JKingWeb\Arsse\REST\AbstractHandler {
'client' => $query['client_id'],
'redir' => $query['redirect_uri'],
'type' => $type,
],\JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE);
], \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE);
// issue an authorization code and build the redirect URL
$code = Arsse::$db->tokenCreate($user, "microsub.auth", null, Date::add("PT2M"), $data);
$next = URL::queryAppend($redir, "code=$code&state=$state");
@ -235,6 +235,10 @@ class Auth extends \JKingWeb\Arsse\REST\AbstractHandler {
*/
protected function opIssueAccessToken(ServerRequestInterface $req): ResponseInterface {
$post = $req->getParsedBody();
// revocation is a special case of POSTing to the token URL
if ($post['action'] ?? "" === "revoke") {
return $this->opRevokeToken($req);
}
if (($post['grant_type'] ?? "") !== "authorization_code") {
throw new ExceptionAuth("unsupported_grant_type");
}
@ -244,7 +248,11 @@ class Auth extends \JKingWeb\Arsse\REST\AbstractHandler {
throw new ExceptionAuth("invalid_grant");
}
// issue an access token
$token = Arsse::$db->tokenCreate($user, "microsub.access");
$data = json_encode([
'me' => $post['me'],
'client_id' => $post['client_id'],
], \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE);
$token = Arsse::$db->tokenCreate($user, "microsub.access", null, null, $data);
Arsse::$db->tokenRevoke($user, "microsub.auth", $post['code']);
$tr->commit();
// return the Bearer token and associated data
@ -285,14 +293,55 @@ class Auth extends \JKingWeb\Arsse\REST\AbstractHandler {
return [$token['user'], $data['type'] ?? "id"];
}
protected function opTokenVerification(string $user, ServerRequestInterface $req): ResponseInterface {
/** Handles token verification as an API call
*
* The static `validateBearer` method should be used to check the validity of a bearer token in normal use
*
* @see https://indieauth.spec.indieweb.org/#access-token-verification
*/
protected function opTokenVerification(ServerRequestInterface $req): ResponseInterface {
try {
if (!$req->hasHeader("Authorization")) {
throw new ExceptionAuth("invalid_token");
}
$authorization = $req->getHeader("Authorization")[0];
list($user, $data) = self::validateBearer($authorization);
} catch (ExceptionAuth $e) {
$errCode = $e->getMessage();
$httpCode = [
'invalid_request' => 400,
'invalid_token' => 401,
][$errCode] ?? 500;
return new EmptyResponse($httpCode, ['WWW-Authenticate' => "Bearer error=\"$erroCode\""]);
}
return new JsonResponse([
'me' => $data['me'] ?? "",
'client_id' => $data['client_id'] ?? "",
'scope' => $data['scope'] ?? self::SCOPES,
]);
}
/** Checks that the simplied bearer token is valid
/** Handles token revocation
*
* Returns an indexed array with the user associated with the token, as well as the granted scope
* @see https://indieauth.spec.indieweb.org/#token-revocation
*/
protected function opRevokeToken(ServerRequestInterface $req): ResponseInterface {
$token = ($req->getParsedBody() ?? [])['token'] ?? "";
if (!strlen($token)) {
return new EmptyResponse(422);
}
try {
$info = Arsse::$db->tokenLookup("microsub.access", $token);
Arsse::$db->tokenRevoke($info['user'], "mucrosub.access", $token);
} catch (\JKingWeb\Arsse\Db\ExceptionInput $e) {
}
return new EmptyResponse(200);
}
/** Checks that the supplied bearer token is valid i.e. logs a bearer in
*
* Returns an indexed array with the user associated with the token, as well as other data
*
* @throws \JKingWeb\Arsse\REST\Microsub\ExceptionAuth
*/
public static function validateBearer(string $authorization, array $scopes = []): array {
@ -305,10 +354,12 @@ class Auth extends \JKingWeb\Arsse\REST\AbstractHandler {
} catch (\JKingWeb\Arsse\Db\ExceptionInput $e) {
throw new ExceptionAuth("invalid_token");
}
$data = @json_decode($token['data'], true) ?? [];
$data['scope'] = $data['scope'] ?? self::SCOPES;
// scope is hard-coded for now
if (array_diff($scopes, self::SCOPES)) {
if (array_diff($scopes, $data['scope'])) {
throw new ExceptionAuth("insufficient_scope");
}
return [$token['user'], self::SCOPES];
return [$token['user'], $data];
}
}

Loading…
Cancel
Save