Move external secrets behind auth broker
This commit is contained in:
@@ -7,8 +7,6 @@ class AppEnvironment {
|
||||
required this.organizationApiBaseUrl,
|
||||
required this.orgContextApiBaseUrl,
|
||||
required this.orgContextTenantSlug,
|
||||
required this.baronKeyId,
|
||||
required this.baronKeySecret,
|
||||
required this.appVersion,
|
||||
required this.buildTimestamp,
|
||||
});
|
||||
@@ -43,15 +41,6 @@ class AppEnvironment {
|
||||
'TDC114_ORG_CONTEXT_TENANT_SLUG',
|
||||
defaultValue: 'hanmac-family',
|
||||
);
|
||||
const baronKeyId = String.fromEnvironment(
|
||||
'TDC114_BARON_KEY_ID',
|
||||
defaultValue: '',
|
||||
);
|
||||
const baronKeySecret = String.fromEnvironment(
|
||||
'TDC114_BARON_KEY_SECRET',
|
||||
defaultValue: '',
|
||||
);
|
||||
|
||||
return AppEnvironment(
|
||||
ssoBaseUrl: ssoBaseUrl,
|
||||
apiBaseUrl: sharedApiBaseUrl,
|
||||
@@ -70,8 +59,6 @@ class AppEnvironment {
|
||||
: organizationApiBaseOverride)
|
||||
: orgContextApiBaseOverride,
|
||||
orgContextTenantSlug: orgContextTenantSlug,
|
||||
baronKeyId: baronKeyId,
|
||||
baronKeySecret: baronKeySecret,
|
||||
appVersion: const String.fromEnvironment(
|
||||
'APP_VERSION',
|
||||
defaultValue: '0.1.0',
|
||||
@@ -90,8 +77,6 @@ class AppEnvironment {
|
||||
final String organizationApiBaseUrl;
|
||||
final String orgContextApiBaseUrl;
|
||||
final String orgContextTenantSlug;
|
||||
final String baronKeyId;
|
||||
final String baronKeySecret;
|
||||
final String appVersion;
|
||||
final String buildTimestamp;
|
||||
}
|
||||
|
||||
@@ -12,8 +12,6 @@ class AuthSessionStore {
|
||||
static const _tokenKey = 'tdc114plus.auth.token';
|
||||
static const _expiresAtKey = 'tdc114plus.auth.expiresAt';
|
||||
static const _userKey = 'tdc114plus.auth.user';
|
||||
static const _orgContextCredentialKey =
|
||||
'tdc114plus.auth.orgContextCredential';
|
||||
static const _pendingRefKey = 'tdc114plus.auth.pending.ref';
|
||||
static const _pendingExpiresAtKey = 'tdc114plus.auth.pending.expiresAt';
|
||||
static const _pendingResendAfterAtKey =
|
||||
@@ -30,17 +28,8 @@ class AuthSessionStore {
|
||||
response.expiresAt.toUtc().toIso8601String(),
|
||||
);
|
||||
await prefs.setString(_userKey, jsonEncode(response.user.toJson()));
|
||||
final orgContextCredential = response.orgContextCredential;
|
||||
if (orgContextCredential == null) {
|
||||
await prefs.remove(_orgContextCredentialKey);
|
||||
} else {
|
||||
await prefs.setString(
|
||||
_orgContextCredentialKey,
|
||||
jsonEncode(orgContextCredential.toJson()),
|
||||
);
|
||||
}
|
||||
debugPrint(
|
||||
'AuthSessionStore.save token=${response.token} tokenLength=${response.token.length} expiresAt=${response.expiresAt.toUtc().toIso8601String()} orgContextCredential=${orgContextCredential != null}',
|
||||
'AuthSessionStore.save token=${response.token} tokenLength=${response.token.length} expiresAt=${response.expiresAt.toUtc().toIso8601String()}',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -113,7 +102,6 @@ class AuthSessionStore {
|
||||
final token = prefs.getString(_tokenKey);
|
||||
final expiresAtValue = prefs.getString(_expiresAtKey);
|
||||
final userValue = prefs.getString(_userKey);
|
||||
final orgContextCredentialValue = prefs.getString(_orgContextCredentialKey);
|
||||
if (token == null || expiresAtValue == null || userValue == null) {
|
||||
debugPrint(
|
||||
'AuthSessionStore.load missing token=${token != null} expiresAt=${expiresAtValue != null} user=${userValue != null}',
|
||||
@@ -124,12 +112,9 @@ class AuthSessionStore {
|
||||
token: token,
|
||||
expiresAt: DateTime.parse(expiresAtValue),
|
||||
user: LoginUser.fromJson(jsonDecode(userValue) as Map<String, dynamic>),
|
||||
orgContextCredential: _decodeOrgContextCredential(
|
||||
orgContextCredentialValue,
|
||||
),
|
||||
);
|
||||
debugPrint(
|
||||
'AuthSessionStore.load token=${session.token} tokenLength=${session.token.length} expiresAt=${session.expiresAt.toUtc().toIso8601String()} expired=${session.isExpired} orgContextCredential=${session.orgContextCredential != null}',
|
||||
'AuthSessionStore.load token=${session.token} tokenLength=${session.token.length} expiresAt=${session.expiresAt.toUtc().toIso8601String()} expired=${session.isExpired}',
|
||||
);
|
||||
return session;
|
||||
}
|
||||
@@ -139,22 +124,9 @@ class AuthSessionStore {
|
||||
await prefs.remove(_tokenKey);
|
||||
await prefs.remove(_expiresAtKey);
|
||||
await prefs.remove(_userKey);
|
||||
await prefs.remove(_orgContextCredentialKey);
|
||||
await clearPendingLink();
|
||||
debugPrint('AuthSessionStore.clear');
|
||||
}
|
||||
|
||||
OrgContextCredential? _decodeOrgContextCredential(String? value) {
|
||||
if (value == null || value.trim().isEmpty) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return OrgContextCredential.fromJsonOrNull(jsonDecode(value));
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class StoredAuthSession {
|
||||
@@ -162,13 +134,11 @@ class StoredAuthSession {
|
||||
required this.token,
|
||||
required this.expiresAt,
|
||||
required this.user,
|
||||
this.orgContextCredential,
|
||||
});
|
||||
|
||||
final String token;
|
||||
final DateTime expiresAt;
|
||||
final LoginUser user;
|
||||
final OrgContextCredential? orgContextCredential;
|
||||
|
||||
bool get isExpired => !expiresAt.isAfter(DateTime.now().toUtc());
|
||||
}
|
||||
|
||||
@@ -101,14 +101,12 @@ class PhoneLoginResponse {
|
||||
required this.token,
|
||||
required this.expiresAt,
|
||||
required this.user,
|
||||
this.orgContextCredential,
|
||||
});
|
||||
|
||||
final String status;
|
||||
final String token;
|
||||
final DateTime expiresAt;
|
||||
final LoginUser user;
|
||||
final OrgContextCredential? orgContextCredential;
|
||||
|
||||
factory PhoneLoginResponse.fromJson(Map<String, dynamic> json) {
|
||||
return PhoneLoginResponse(
|
||||
@@ -116,9 +114,6 @@ class PhoneLoginResponse {
|
||||
token: json['token'] as String? ?? json['accessToken'] as String? ?? '',
|
||||
expiresAt: DateTime.parse(json['expiresAt'] as String),
|
||||
user: LoginUser.fromJson(json['user'] as Map<String, dynamic>? ?? {}),
|
||||
orgContextCredential: OrgContextCredential.fromJsonOrNull(
|
||||
json['orgContextCredential'] ?? json['org_context'],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -128,83 +123,10 @@ class PhoneLoginResponse {
|
||||
'token': token,
|
||||
'expiresAt': expiresAt.toUtc().toIso8601String(),
|
||||
'user': user.toJson(),
|
||||
if (orgContextCredential != null)
|
||||
'orgContextCredential': orgContextCredential!.toJson(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class OrgContextCredential {
|
||||
const OrgContextCredential({
|
||||
required this.baseUrl,
|
||||
required this.tenantSlug,
|
||||
required this.keyId,
|
||||
required this.keySecret,
|
||||
this.expiresAt,
|
||||
});
|
||||
|
||||
final String baseUrl;
|
||||
final String tenantSlug;
|
||||
final String keyId;
|
||||
final String keySecret;
|
||||
final DateTime? expiresAt;
|
||||
|
||||
bool get isUsable {
|
||||
return baseUrl.trim().isNotEmpty &&
|
||||
tenantSlug.trim().isNotEmpty &&
|
||||
keyId.trim().isNotEmpty &&
|
||||
keySecret.trim().isNotEmpty &&
|
||||
(expiresAt == null || expiresAt!.isAfter(DateTime.now().toUtc()));
|
||||
}
|
||||
|
||||
factory OrgContextCredential.fromJson(Map<String, dynamic> json) {
|
||||
return OrgContextCredential(
|
||||
baseUrl: _readString(json, const ['baseUrl', 'base_url']),
|
||||
tenantSlug: _readString(json, const ['tenantSlug', 'tenant_slug']),
|
||||
keyId: _readString(json, const ['keyId', 'key_id']),
|
||||
keySecret: _readString(json, const ['keySecret', 'key_secret']),
|
||||
expiresAt: _readDate(json['expiresAt'] ?? json['expires_at']),
|
||||
);
|
||||
}
|
||||
|
||||
static OrgContextCredential? fromJsonOrNull(Object? value) {
|
||||
if (value is! Map) {
|
||||
return null;
|
||||
}
|
||||
final credential = OrgContextCredential.fromJson(
|
||||
Map<String, dynamic>.from(value),
|
||||
);
|
||||
return credential.isUsable ? credential : null;
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'baseUrl': baseUrl,
|
||||
'tenantSlug': tenantSlug,
|
||||
'keyId': keyId,
|
||||
'keySecret': keySecret,
|
||||
if (expiresAt != null) 'expiresAt': expiresAt!.toUtc().toIso8601String(),
|
||||
};
|
||||
}
|
||||
|
||||
static String _readString(Map<String, dynamic> json, List<String> keys) {
|
||||
for (final key in keys) {
|
||||
final value = json[key];
|
||||
if (value is String && value.trim().isNotEmpty) {
|
||||
return value.trim();
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
static DateTime? _readDate(Object? value) {
|
||||
if (value is! String || value.trim().isEmpty) {
|
||||
return null;
|
||||
}
|
||||
return DateTime.tryParse(value)?.toUtc();
|
||||
}
|
||||
}
|
||||
|
||||
class PhoneLoginLinkInitRequest {
|
||||
const PhoneLoginLinkInitRequest({
|
||||
required this.phoneNumber,
|
||||
|
||||
@@ -16,8 +16,6 @@ class OrgContextApiClient {
|
||||
const OrgContextApiClient({
|
||||
required this.httpClient,
|
||||
required this.baseUri,
|
||||
required this.keyId,
|
||||
required this.keySecret,
|
||||
required this.tenantSlug,
|
||||
this.sessionStore,
|
||||
this.timeout = const Duration(seconds: 15),
|
||||
@@ -28,8 +26,6 @@ class OrgContextApiClient {
|
||||
|
||||
final http.Client httpClient;
|
||||
final Uri baseUri;
|
||||
final String keyId;
|
||||
final String keySecret;
|
||||
final String tenantSlug;
|
||||
final AuthSessionStore? sessionStore;
|
||||
final Duration timeout;
|
||||
@@ -91,27 +87,13 @@ class OrgContextApiClient {
|
||||
String? tenantSlug,
|
||||
}) async {
|
||||
final session = await _session();
|
||||
final sessionCredential = session?.orgContextCredential;
|
||||
final requestedTenantSlug = tenantSlug?.trim();
|
||||
if (sessionCredential != null && sessionCredential.isUsable) {
|
||||
return _OrgContextCredentialSnapshot(
|
||||
baseUri: Uri.parse(sessionCredential.baseUrl),
|
||||
tenantSlug: requestedTenantSlug == null || requestedTenantSlug.isEmpty
|
||||
? sessionCredential.tenantSlug
|
||||
: requestedTenantSlug,
|
||||
keyId: sessionCredential.keyId,
|
||||
keySecret: sessionCredential.keySecret,
|
||||
appSessionToken: session?.token ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
return _OrgContextCredentialSnapshot(
|
||||
baseUri: baseUri,
|
||||
tenantSlug: requestedTenantSlug == null || requestedTenantSlug.isEmpty
|
||||
? this.tenantSlug
|
||||
: requestedTenantSlug,
|
||||
keyId: keyId,
|
||||
keySecret: keySecret,
|
||||
appSessionToken: session?.token ?? '',
|
||||
);
|
||||
}
|
||||
@@ -141,10 +123,6 @@ class OrgContextApiClient {
|
||||
'accept': 'application/json',
|
||||
if (appSessionToken.isNotEmpty) 'Authorization': 'Bearer $appSessionToken',
|
||||
if (appSessionToken.isNotEmpty) 'X-App-Session': appSessionToken,
|
||||
if (credential.keyId.trim().isNotEmpty)
|
||||
'X-Baron-Key-ID': credential.keyId.trim(),
|
||||
if (credential.keySecret.trim().isNotEmpty)
|
||||
'X-Baron-Key-Secret': credential.keySecret.trim(),
|
||||
};
|
||||
debugPrint(
|
||||
'OrgContextApiClient._headers headers=$headers tenantSlug=${credential.tenantSlug} base=${credential.baseUri}',
|
||||
@@ -458,8 +436,6 @@ final orgContextApiClientProvider = Provider<OrgContextApiClient>((ref) {
|
||||
return OrgContextApiClient(
|
||||
httpClient: ref.watch(httpClientProvider),
|
||||
baseUri: Uri.parse(environment.orgContextApiBaseUrl),
|
||||
keyId: environment.baronKeyId,
|
||||
keySecret: environment.baronKeySecret,
|
||||
tenantSlug: environment.orgContextTenantSlug,
|
||||
sessionStore: ref.watch(authSessionStoreProvider),
|
||||
);
|
||||
@@ -475,22 +451,18 @@ class _OrgContextCredentialSnapshot {
|
||||
const _OrgContextCredentialSnapshot({
|
||||
required this.baseUri,
|
||||
required this.tenantSlug,
|
||||
required this.keyId,
|
||||
required this.keySecret,
|
||||
required this.appSessionToken,
|
||||
});
|
||||
|
||||
final Uri baseUri;
|
||||
final String tenantSlug;
|
||||
final String keyId;
|
||||
final String keySecret;
|
||||
final String appSessionToken;
|
||||
|
||||
String get cacheKey {
|
||||
return [
|
||||
baseUri.toString(),
|
||||
tenantSlug,
|
||||
appSessionToken.isEmpty ? keyId : appSessionToken,
|
||||
appSessionToken,
|
||||
].join('|');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user