xp-forge / web-auth Goto Github PK
View Code? Open in Web Editor NEWWeb Authentication
Web Authentication
This occurs when authentication fails - some providers decide not to shown an error page on their own but to redirect back to the original page and pass on an error response:
If the request fails due to a missing, invalid, or mismatching redirection URI, or if the client identifier is missing or invalid, the authorization server SHOULD inform the resource owner of the error and MUST NOT automatically redirect the user-agent to the invalid redirection URI.
By omitting the lookup function, we essentially return the BySignedRequests
or ByAccessToken
instances. These should be serialized properly to be able to reuse them with e.g. the classes from xp-forge/rest-client
.
use web\auth\SessionBased;
use web\auth\oauth\OAuth2Flow;
use web\session\ForTesting;
use webservices\rest\Endpoint;
$flow= new OAuth2Flow(
'https://github.com/login/oauth/authorize',
'https://github.com/login/oauth/access_token',
[$credentials->named('github_oauth_key'), $credentials->named('github_oauth_secret')],
$callback
);
$auth= new SessionBased($flow, new ForTesting());
return ['/' => $auth->required(function($req, $res) {
$endpoint= (new Endpoint('https://api.github.com/'))->with('Authorization', $req->value('user'));
$user= $endpoint->resource('user')->get()->value();
$res->send('Hello @'.$user['login'], 'text/html');
})];
Currently, this is the typical code setup inside a web applications' routes() method:
use web\auth\SessionBased;
use web\auth\oauth\{OAuth2Flow, BySecret};
$flow= new OAuth2Flow(
'http://localhost:8443/oauth/common/authorize',
'http://localhost:8443/oauth/common/token',
new BySecret('613aacd1f95ce7ee1b04', '...'),
'/',
['user'],
);
$auth= new SessionBased($flow, $sessions, function($client) use($users) {
$me= $client->fetch('http://localhost:8443/graph/me')->value();
return $users->upsert([
'handle' => $me['id'],
'name' => $me['name'],
// continues mapping fields, shortened for brevity
]);
});
return ['/' => $auth->required($frontend)];
If I want to make this configurable and integrate well with several services, there's a lot to do:
๐ This boils down to almost 10 configuration options that have to be set in order to get an OAuth workflow running!
Flow error, session state 4c931222a3682e22ae3cc5484ee6d89f != server state a29fc8d9132ca9fa0610cb9ff16b5dd2
Unclear what's causing this. Can only be fixed by deleting cookies
Currently web handlers have access to the authenticated user via the request value named user
, which is returned by the authentication flow. However, the following use-cases are not possible without workarounds:
To make this possible, a new value authentication
could be passed to the request containing methods to access user and session.
From a Microsoft Graph specific implementation:
use peer\URL;
use util\{UUID, Secret};
use lang\IllegalStateException;
abstract class Credentials {
protected $tenant, $client;
public function __construct($tenant, $client) {
$this->tenant= $tenant;
$this->client= $client;
}
/** @return string */
public function tenant() { return $this->tenant; }
/** @return string */
public function client() { return $this->client; }
/**
* Returns request parameters for a given endpoint
*
* @param peer.URL $endpoint
* @return [:string]
*/
public abstract function request(URL $endpoint);
}
class ClientSecret extends Credentials {
private $secret;
public function __construct($tenant, $client, $secret) {
parent::__construct($tenant, $client);
$this->secret= $secret instanceof Secret ? $secret : new Secret($secret);
}
public function request(URL $endpoint) {
return ['client_id' => $this->client, 'client_secret' => $this->secret->reveal()];
}
}
class WithCertificate extends Credentials {
private $certificate, $key;
public function __construct($tenant, $client, $certificate, $key) {
parent::__construct($tenant, $client);
$this->certificate= $certificate;
$this->key= $key;
}
public function request(URL $endpoint) {
$time= time();
$jwt= new JWT(['alg' => 'RS256', 'typ' => 'JWT', 'x5t' => JWT::base64(hex2bin($this->certificate))], [
'aud' => $endpoint->toString(),
'exp' => $time + 3600,
'iss' => $this->client,
'jti' => UUID::timeUUID()->hashCode(),
'nbf' => $time,
'sub' => $this->client,
]);
return [
'client_id' => $this->client,
'client_assertion' => $jwt->sign($this->key),
'client_assertion_type' => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
];
}
}
/**
* Very simple JWT implementation (only supporting `RS256`)
*
* @see https://tools.ietf.org/html/rfc7519
* @ext openssl
*/
class JWT {
private $header, $payload;
/** Creates a new JWT with a given header and payload */
public function __construct(array $header, array $payload) {
$this->header= $header;
$this->payload= $payload;
}
/** URL-safe Base64 encoding */
public static function base64(string $bytes): string {
return strtr(rtrim(base64_encode($bytes), '='), '+/', '-_');
}
/** Sign JWT and return token */
public function sign(string $key): string {
$input= self::base64(json_encode($this->header)).'.'.self::base64(json_encode($this->payload));
// Hardcode SHA256 signing via OpenSSL here, would need algorithm-based
// handling in order for this to be a full implementation, see e.g.
// https://github.com/firebase/php-jwt/blob/v6.2.0/src/JWT.php#L220
if (!openssl_sign($input, $signature, openssl_pkey_get_private($key), 'SHA256')) {
throw new IllegalStateException(openssl_error_string());
}
return $input.'.'.self::base64($signature);
}
}
See:
When using a URL with a path other than the one registered, Twitter yields an error:
Registered callback URLs: http://localhost:8080/, http://localhost:8080/twitter/
Using LinkedIn OAuth this error appears after authenticating in line 93 of OAuth2Flow.class.php
91 return new ByAccessToken(
92 $stored['access_token'],
93 $stored['token_type'],
94 $stored['scope'] ?? null,
95 $stored['expires_in'] ?? null,
96 $stored['refresh_token'] ?? null
97 );
This is because the hash returned by LinkedIn only contains the keys access_token
and expires_in
.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.