• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wger-project / flutter / 30854254088

03 Aug 2026 09:21PM UTC coverage: 40.895% (-15.0%) from 55.93%
30854254088

Pull #1260

github

web-flow
Merge d93794de5 into e3affdd4f
Pull Request #1260: WIP: health sync

9980 of 24404 relevant lines covered (40.89%)

1.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.5
/lib/core/network/jwt.dart
1
/*
2
 * This file is part of wger Workout Manager <https://github.com/wger-project>.
3
 * Copyright (c) 2020 - 2026 wger Team
4
 *
5
 * wger Workout Manager is free software: you can redistribute it and/or modify
6
 * it under the terms of the GNU Affero General Public License as published by
7
 * the Free Software Foundation, either version 3 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU Affero General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Affero General Public License
16
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
 */
18

19
import 'dart:convert';
20

21
import 'package:logging/logging.dart';
22

23
final _logger = Logger('jwt');
×
24

25
/// Decodes the payload (middle segment) of a JWT.
26
///
27
/// Returns null when the input is not a three-segment token, when the middle
28
/// segment is not valid base64-url, or when the decoded bytes are not a JSON
29
/// object. The signature is not verified, so callers must treat the result
30
/// as untrusted data.
31
Map<String, dynamic>? decodeJwtPayload(String jwt) {
3✔
32
  try {
33
    final parts = jwt.split('.');
3✔
34
    if (parts.length != 3) {
6✔
35
      return null;
36
    }
37
    return json.decode(utf8.decode(base64Url.decode(base64Url.normalize(parts[1]))))
15✔
38
        as Map<String, dynamic>;
39
  } catch (e) {
40
    _logger.warning('Could not decode JWT payload', e);
×
41
    return null;
42
  }
43
}
44

45
/// Extracts the `exp` claim (unix seconds) as a UTC [DateTime].
46
///
47
/// Returns null when the payload is null, when `exp` is missing, or when it is
48
/// not numeric. Fractional seconds are truncated.
49
DateTime? jwtExp(Map<String, dynamic>? payload) {
3✔
50
  final exp = payload?['exp'];
3✔
51
  if (exp is! num) {
3✔
52
    return null;
53
  }
54
  return DateTime.fromMillisecondsSinceEpoch(exp.toInt() * 1000, isUtc: true);
9✔
55
}
56

57
/// Projects the token lifetime (`exp - iat`) onto the local clock.
58
///
59
/// The absolute `exp` claim is server time; comparing it against the device
60
/// clock (as consumers of an expiry timestamp do) breaks expiry scheduling
61
/// when that clock is off. Taking only the lifetime from the server keeps
62
/// the schedule correct regardless of clock skew.
63
///
64
/// Falls back to the absolute `exp` when `iat` is missing or not numeric.
65
DateTime? jwtExpOnLocalClock(Map<String, dynamic>? payload) {
2✔
66
  final iat = payload?['iat'];
2✔
67
  final exp = payload?['exp'];
2✔
68
  if (iat is! num || exp is! num) {
4✔
69
    return jwtExp(payload);
1✔
70
  }
71
  return DateTime.now().toUtc().add(Duration(seconds: (exp - iat).toInt()));
6✔
72
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc