diff --git a/example/cookie_signer.dart b/example/cookie_signer.dart new file mode 100644 index 00000000..48256f2e --- /dev/null +++ b/example/cookie_signer.dart @@ -0,0 +1,59 @@ +import 'dart:io'; +import 'dart:math'; +import 'package:angel_framework/angel_framework.dart'; +import 'package:angel_framework/http.dart'; +import 'package:angel_security/angel_security.dart'; +import 'package:http_parser/http_parser.dart'; +import 'package:logging/logging.dart'; +import 'package:pretty_logging/pretty_logging.dart'; + +main() async { + // Logging boilerplate. + Logger.root.onRecord.listen(prettyLog); + + // Create an app, and HTTP driver. + var app = Angel(logger: Logger('cookie_signer')), http = AngelHttp(app); + + // Create a cookie signer. Uses an SHA256 Hmac by default. + var signer = CookieSigner.fromStringKey( + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789ab'); + + // When a user visits /getid, give them a (signed) uniqid cookie. + // When they visit /cookies, print their verified cookies. + var rnd = Random.secure(); + + // Endpoint to give a signed cookie. + app.get('/getid', (req, res) { + // Write the uniqid cookie. + var uniqid = rnd.nextInt(65536); + signer.writeCookies(res, [ + Cookie('uniqid', uniqid.toString()), + ]); + + // Send a response. + res.write('uniqid=$uniqid'); + }); + + // Endpoint to dump all verified cookies. + // + // The [onInvalidCookie] callback is optional, but + // here we will use it to log invalid cookies. + app.get('/cookies', (req, res) { + var verifiedCookies = signer.readCookies(req, onInvalidCookie: (cookie) { + app.logger.warning('Invalid cookie: $cookie'); + }); + res.writeln('${verifiedCookies.length} verified cookie(s)'); + res.writeln('${req.cookies.length} total unverified cookie(s)'); + for (var cookie in verifiedCookies) { + res.writeln('${cookie.name}=${cookie.value}'); + } + }); + + // 404 otherwise. + app.fallback((req, res) => throw AngelHttpException.notFound( + message: 'The only valid endpoints are /getid and /cookies.')); + + // Start the server. + await http.startServer('127.0.0.1', 3000); + print('Cookie signer example listening at ${http.uri}'); +} diff --git a/lib/src/cookie_signer.dart b/lib/src/cookie_signer.dart index 51493b4c..dbb1f553 100644 --- a/lib/src/cookie_signer.dart +++ b/lib/src/cookie_signer.dart @@ -90,12 +90,13 @@ class CookieSigner { /// Returns a new cookie, replacing the value of an input /// [cookie] with one that is signed with the [hmac]. /// - /// The signature is: - /// `base64Url(cookie.value) + "." + base64Url(sig)` + /// The new value is: + /// `cookie.value + "." + base64Url(sig)` /// /// Where `sig` is the cookie's value, signed with the [hmac]. Cookie createSignedCookie(Cookie cookie) { - return cookieWithNewValue(cookie, computeCookieSignature(cookie.value)); + return cookieWithNewValue( + cookie, cookie.value + '.' + computeCookieSignature(cookie.value)); } /// Returns a new [Cookie] that is the same as the input @@ -110,13 +111,14 @@ class CookieSigner { ..secure = cookie.secure; } - /// Computes the signature of a [cookieValue], either for + /// Computes the *signature* of a [cookieValue], either for /// signing an outgoing cookie, or verifying an incoming cookie. String computeCookieSignature(String cookieValue) { // base64Url(cookie) + "." + base64Url(sig) - var encodedCookie = base64Url.encode(cookieValue.codeUnits); + // var encodedCookie = base64Url.encode(cookieValue.codeUnits); var sigBytes = hmac.convert(cookieValue.codeUnits).bytes; - var sig = base64Url.encode(sigBytes); - return encodedCookie + '.' + sig; + return base64Url.encode(sigBytes); + // var sig = base64Url.encode(sigBytes); + // return encodedCookie + '.' + sig; } }