LCOV - code coverage report
Current view: top level - core/util - token_helper.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 50 55 90.9 %
Date: 2021-04-14 08:03:52 Functions: 0 0 -

          Line data    Source code
       1             : import 'dart:convert';
       2             : import 'package:jose/jose.dart';
       3             : import 'package:stream_feed_dart/src/core/http/token.dart';
       4             : import 'package:stream_feed_dart/src/core/models/feed_id.dart';
       5             : 
       6             : /// Actions permissions
       7          14 : enum TokenAction {
       8             :   /// allows any operations
       9             :   any,
      10             : 
      11             :   /// allows read operations
      12             :   read,
      13             : 
      14             :   /// allows write operations
      15             :   write,
      16             : 
      17             :   /// allows delete operations
      18             :   delete,
      19             : }
      20             : 
      21             : extension TokenActionX on TokenAction {
      22           6 :   String? get action => {
      23             :         TokenAction.any: '*',
      24             :         TokenAction.read: 'read',
      25             :         TokenAction.write: 'write',
      26             :         TokenAction.delete: 'delete',
      27           3 :       }[this];
      28             : }
      29             : 
      30             : /// Resource Access Restrictions
      31          14 : enum TokenResource {
      32             :   /// allows access to any resource
      33             :   any,
      34             : 
      35             :   /// allows access to [Activity] resource
      36             :   activities,
      37             : 
      38             :   /// allows access to analytics resource
      39             :   analytics,
      40             : 
      41             :   /// allows access to analyticsRedirect resource
      42             :   analyticsRedirect,
      43             : 
      44             :   /// allows access to [CollectionEntry] resource
      45             :   collections,
      46             : 
      47             :   /// allows access to files resource
      48             :   files,
      49             : 
      50             :   /// allows access to [Feed] resource
      51             :   feed,
      52             : 
      53             :   /// allows access to feedTargets resource
      54             :   feedTargets,
      55             : 
      56             :   /// allows access to [Follow] resource
      57             :   follower,
      58             : 
      59             :   /// allows access to [OpenGraph] resource
      60             :   openGraph,
      61             : 
      62             :   /// token resource that allows access to personalization resource
      63             :   personalization,
      64             : 
      65             :   /// token resource that allows access to [Reaction] resource
      66             :   reactions,
      67             : 
      68             :   /// token resource that allows access to [User] resource
      69             :   users,
      70             : }
      71             : 
      72             : /// Convenient class Extension to on [TokenResource] enum
      73             : extension TokenResourceX on TokenResource {
      74             :   /// Convenient method Extension to stringify the [TokenResource] enum
      75           6 :   String? get resource => {
      76             :         TokenResource.any: '*',
      77             :         TokenResource.activities: 'activities',
      78             :         TokenResource.analytics: 'analytics',
      79             :         TokenResource.analyticsRedirect: 'redirect_and_track',
      80             :         TokenResource.collections: 'collections',
      81             :         TokenResource.files: 'files',
      82             :         TokenResource.feed: 'feed',
      83             :         TokenResource.feedTargets: 'feed_targets',
      84             :         TokenResource.follower: 'follower',
      85             :         TokenResource.openGraph: 'url',
      86             :         TokenResource.personalization: 'ppersonalization',
      87             :         TokenResource.reactions: 'reactions',
      88             :         TokenResource.users: 'users',
      89           3 :       }[this];
      90             : }
      91             : 
      92             : class TokenHelper {
      93           0 :   const TokenHelper();
      94             : 
      95           1 :   static Token buildFeedToken(
      96             :     String secret,
      97             :     TokenAction action, [
      98             :     FeedId? feed,
      99             :   ]) =>
     100           1 :       _buildBackendToken(
     101           0 :           secret, TokenResource.feed, action, feed?.claim ?? '*');
     102             : 
     103           1 :   static Token buildFollowToken(
     104             :     String secret,
     105             :     TokenAction action, [
     106             :     FeedId? feed,
     107             :   ]) =>
     108           1 :       _buildBackendToken(
     109           0 :           secret, TokenResource.follower, action, feed?.claim ?? '*');
     110             : 
     111           1 :   static Token buildReactionToken(String secret, TokenAction action) =>
     112           1 :       _buildBackendToken(secret, TokenResource.reactions, action, '*');
     113             : 
     114           1 :   static Token buildActivityToken(String secret, TokenAction action) =>
     115           1 :       _buildBackendToken(secret, TokenResource.activities, action, '*');
     116             : 
     117           1 :   static Token buildUsersToken(String secret, TokenAction action) =>
     118           1 :       _buildBackendToken(secret, TokenResource.users, action, '*');
     119             : 
     120           2 :   static Token buildCollectionsToken(String secret, TokenAction action) =>
     121           2 :       _buildBackendToken(secret, TokenResource.collections, action, '*');
     122             : 
     123           4 :   static Token buildOpenGraphToken(String secret) => _buildBackendToken(
     124             :       secret, TokenResource.openGraph, TokenAction.read, '*');
     125             : 
     126           1 :   static Token buildToTargetUpdateToken(
     127             :     String secret,
     128             :     TokenAction action, [
     129             :     FeedId? feed,
     130             :   ]) =>
     131           1 :       _buildBackendToken(
     132           0 :           secret, TokenResource.feedTargets, action, feed?.claim ?? '*');
     133             : 
     134           1 :   static Token buildFilesToken(String secret, TokenAction action) =>
     135           1 :       _buildBackendToken(secret, TokenResource.files, action, '*');
     136             : 
     137           2 :   static Token buildFrontendToken(
     138             :     String secret,
     139             :     String userId, {
     140             :     DateTime? expiresAt,
     141             :   }) {
     142           2 :     final claims = <String, Object>{
     143             :       'user_id': userId,
     144             :     };
     145             : 
     146           2 :     return Token(
     147           2 :         issueJwtHS256(secret: secret, expiresAt: expiresAt, claims: claims));
     148             :   }
     149             : 
     150             :   /// Creates the JWT token for [feedId], [resource] and [action]
     151             :   /// using the api [secret]
     152           3 :   static Token _buildBackendToken(
     153             :     String secret,
     154             :     TokenResource resource,
     155             :     TokenAction action,
     156             :     String feedId, {
     157             :     String? userId,
     158             :   }) {
     159           3 :     final claims = <String, Object?>{
     160           3 :       'resource': resource.resource,
     161           3 :       'action': action.action,
     162             :       'feed_id': feedId,
     163             :     };
     164             :     if (userId != null) {
     165           0 :       claims['user_id'] = userId;
     166             :     }
     167             : 
     168           6 :     return Token(issueJwtHS256(secret: secret, claims: claims));
     169             :   }
     170             : }
     171             : 
     172           3 : String issueJwtHS256({
     173             :   required String secret,
     174             :   required Map<String, Object?>? claims,
     175             :   DateTime? expiresAt,
     176             : }) {
     177           6 :   final claimSet = JsonWebTokenClaims.fromJson({
     178           6 :     'exp': DateTime.now()
     179           3 :             .add(const Duration(seconds: 1200))
     180           6 :             .millisecondsSinceEpoch ~/
     181             :         1000,
     182          15 :     'iat': DateTime.now().toUtc().millisecondsSinceEpoch ~/ 1000,
     183           3 :     if (claims != null) ...claims,
     184             :   });
     185             : 
     186             :   // create a builder, decoding the JWT in a JWS, so using a
     187             :   // JsonWebSignatureBuilder
     188           3 :   final builder = JsonWebSignatureBuilder()
     189             : 
     190             :     // set the content
     191           6 :     ..jsonContent = claimSet.toJson()
     192             : 
     193             :     // add a key to sign, can only add one for JWT
     194           3 :     ..addRecipient(
     195           6 :         JsonWebKey.fromJson({
     196             :           'kty': 'oct',
     197           3 :           'k': base64Urlencode(secret),
     198             :         }),
     199             :         algorithm: 'HS256')
     200             :     // builder.recipients
     201           3 :     ..setProtectedHeader('typ', 'JWT');
     202             :   // build the jws
     203           3 :   final jws = builder.build();
     204             : 
     205             :   // output the compact serialization
     206           3 :   return jws.toCompactSerialization();
     207             : }
     208             : 
     209           3 : String base64Urlencode(String secret) {
     210           3 :   final Codec<String?, String> stringToBase64Url = utf8.fuse(base64Url);
     211           3 :   final encoded = stringToBase64Url.encode(secret);
     212             :   return encoded;
     213             : }

Generated by: LCOV version 1.15