From e163c1b9e9d41cf0406e5c9697388c818feaefa0 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Fri, 20 Jan 2017 18:15:21 -0500 Subject: [PATCH] 18 --- README.md | 2 +- lib/auth_token.dart | 4 ++++ lib/src/auth_token.dart | 35 +++++++++++++++++++++++++++++++++-- lib/src/options.dart | 6 ++++++ lib/src/plugin.dart | 35 +++++++++++++++++++++++++++++++++++ pubspec.yaml | 2 +- 6 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 lib/auth_token.dart diff --git a/README.md b/README.md index f99f9b36..43233938 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # angel_auth -[![version 1.1.0-dev+17](https://img.shields.io/badge/version-1.1.0--dev+17-red.svg)](https://pub.dartlang.org/packages/angel_auth) +[![version 1.1.0-dev+18](https://img.shields.io/badge/version-1.1.0--dev+18-red.svg)](https://pub.dartlang.org/packages/angel_auth) ![build status](https://travis-ci.org/angel-dart/auth.svg?branch=master) A complete authentication plugin for Angel. Inspired by Passport. diff --git a/lib/auth_token.dart b/lib/auth_token.dart new file mode 100644 index 00000000..8273e533 --- /dev/null +++ b/lib/auth_token.dart @@ -0,0 +1,4 @@ +/// Stand-alone JWT library. +library angel_auth.auth_token; + +export 'src/auth_token.dart'; \ No newline at end of file diff --git a/lib/src/auth_token.dart b/lib/src/auth_token.dart index 2a37260b..67b66d83 100644 --- a/lib/src/auth_token.dart +++ b/lib/src/auth_token.dart @@ -3,6 +3,27 @@ import 'dart:convert'; import 'package:angel_framework/angel_framework.dart'; import 'package:crypto/crypto.dart'; +/// Calls [BASE64URL], but also works for strings with lengths +/// that are *not* multiples of 4. +String decodeBase64(String str) { + var output = str.replaceAll('-', '+').replaceAll('_', '/'); + + switch (output.length % 4) { + case 0: + break; + case 2: + output += '=='; + break; + case 3: + output += '='; + break; + default: + throw 'Illegal base64url string!"'; + } + + return UTF8.decode(BASE64URL.decode(output)); +} + class AuthToken { final SplayTreeMap _header = new SplayTreeMap.from({"alg": "HS256", "typ": "JWT"}); @@ -35,14 +56,24 @@ class AuthToken { payload: data["pld"] ?? {}); } + factory AuthToken.parse(String jwt) { + var split = jwt.split("."); + + if (split.length != 3) + throw new AngelHttpException.notAuthenticated(message: "Invalid JWT."); + + var payloadString = decodeBase64(split[1]); + return new AuthToken.fromMap(JSON.decode(payloadString)); + } + factory AuthToken.validate(String jwt, Hmac hmac) { var split = jwt.split("."); if (split.length != 3) throw new AngelHttpException.notAuthenticated(message: "Invalid JWT."); - // var headerString = new String.fromCharCodes(BASE64URL.decode(split[0])); - var payloadString = new String.fromCharCodes(BASE64URL.decode(split[1])); + // var headerString = decodeBase64(split[0]); + var payloadString = decodeBase64(split[1]); var data = split[0] + "." + split[1]; var signature = BASE64URL.encode(hmac.convert(data.codeUnits).bytes); diff --git a/lib/src/options.dart b/lib/src/options.dart index b0088d83..c94fa062 100644 --- a/lib/src/options.dart +++ b/lib/src/options.dart @@ -1,16 +1,22 @@ import 'package:angel_framework/angel_framework.dart'; +import 'auth_token.dart'; typedef AngelAuthCallback( RequestContext req, ResponseContext res, String token); +typedef AngelAuthTokenCallback( + RequestContext req, ResponseContext res, AuthToken token, user); + class AngelAuthOptions { AngelAuthCallback callback; + AngelAuthTokenCallback tokenCallback; bool canRespondWithJson; String successRedirect; String failureRedirect; AngelAuthOptions( {this.callback, + this.tokenCallback, this.canRespondWithJson: true, this.successRedirect, String this.failureRedirect}); diff --git a/lib/src/plugin.dart b/lib/src/plugin.dart index 4d9ca683..16313362 100644 --- a/lib/src/plugin.dart +++ b/lib/src/plugin.dart @@ -26,6 +26,8 @@ class AngelAuth extends AngelPlugin { UserSerializer serializer; UserDeserializer deserializer; + Hmac get hmac => _hs256; + String _randomString( {int length: 32, String validChars: @@ -234,6 +236,13 @@ class AngelAuth extends AngelPlugin { var token = new AuthToken( userId: userId, lifeSpan: _jwtLifeSpan, ipAddress: req.ip); var jwt = token.serialize(_hs256); + + if (options?.tokenCallback != null) { + var r = await options.tokenCallback( + req, res, token, req.properties["user"] = result); + if (r != null) return r; + } + req ..inject(AuthToken, req.properties['token'] = token) ..inject(result.runtimeType, req.properties["user"] = result); @@ -265,6 +274,32 @@ class AngelAuth extends AngelPlugin { throw new AngelHttpException.notAuthenticated(); } + /// Log a user in on-demand. + Future login(AuthToken token, RequestContext req, ResponseContext res) async { + var user = await deserializer(token.userId); + + req + ..inject(AuthToken, req.properties['token'] = token) + ..inject(user.runtimeType, req.properties["user"] = user); + + if (allowCookie) + res.cookies.add(new Cookie('token', token.serialize(_hs256))); + } + + /// Log a user in on-demand. + Future loginById(userId, RequestContext req, ResponseContext res) async { + var user = await deserializer(userId); + var token = new AuthToken( + userId: userId, lifeSpan: _jwtLifeSpan, ipAddress: req.ip); + + req + ..inject(AuthToken, req.properties['token'] = token) + ..inject(user.runtimeType, req.properties["user"] = user); + + if (allowCookie) + res.cookies.add(new Cookie('token', token.serialize(_hs256))); + } + logout([AngelAuthOptions options]) { return (RequestContext req, ResponseContext res) async { for (AuthStrategy strategy in strategies) { diff --git a/pubspec.yaml b/pubspec.yaml index bdf93359..14155f0e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: angel_auth description: A complete authentication plugin for Angel. -version: 1.0.0-dev+17 +version: 1.0.0-dev+18 author: Tobe O homepage: https://github.com/angel-dart/angel_auth environment: