From cb36612b88caafffa441329e893052055a126391 Mon Sep 17 00:00:00 2001 From: Codex Date: Mon, 20 Jul 2026 14:19:55 +0900 Subject: [PATCH] Move external secrets behind auth broker --- .gitignore | 5 ++ app/lib/src/core/config/app_environment.dart | 15 ---- .../auth/data/auth_session_store.dart | 34 +------- .../src/features/auth/domain/auth_models.dart | 78 ------------------- .../data/org_context_api_client.dart | 30 +------ app/test/auth/auth_session_store_test.dart | 9 --- .../org_context_api_client_test.dart | 73 ++++++----------- ...plus_work_progress_timetable_2026-07-02.md | 1 + scripts/README.md | 3 +- scripts/android-device.staging.env.example | 12 ++- scripts/integration_tests.sh | 2 - scripts/manual-postlogin-run.sh | 2 - scripts/smoke.staging.env.example | 10 +-- scripts/start-auth-server.sh | 21 +++-- 14 files changed, 55 insertions(+), 240 deletions(-) diff --git a/.gitignore b/.gitignore index b2d5fbb..23af39f 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,11 @@ Thumbs.db .env .env.* !.env.example +secrets/ +logs/ +temp/ +.tmp/ +backups/ # Local Android/ADB state .android-adb/ diff --git a/app/lib/src/core/config/app_environment.dart b/app/lib/src/core/config/app_environment.dart index 6191cfd..33f6757 100644 --- a/app/lib/src/core/config/app_environment.dart +++ b/app/lib/src/core/config/app_environment.dart @@ -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; } diff --git a/app/lib/src/features/auth/data/auth_session_store.dart b/app/lib/src/features/auth/data/auth_session_store.dart index 62b84f3..f9e40e0 100644 --- a/app/lib/src/features/auth/data/auth_session_store.dart +++ b/app/lib/src/features/auth/data/auth_session_store.dart @@ -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), - 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()); } diff --git a/app/lib/src/features/auth/domain/auth_models.dart b/app/lib/src/features/auth/domain/auth_models.dart index a5ca0f0..b14e12b 100644 --- a/app/lib/src/features/auth/domain/auth_models.dart +++ b/app/lib/src/features/auth/domain/auth_models.dart @@ -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 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? ?? {}), - 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 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.from(value), - ); - return credential.isUsable ? credential : null; - } - - Map toJson() { - return { - 'baseUrl': baseUrl, - 'tenantSlug': tenantSlug, - 'keyId': keyId, - 'keySecret': keySecret, - if (expiresAt != null) 'expiresAt': expiresAt!.toUtc().toIso8601String(), - }; - } - - static String _readString(Map json, List 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, diff --git a/app/lib/src/features/organization/data/org_context_api_client.dart b/app/lib/src/features/organization/data/org_context_api_client.dart index e6da590..bf497a0 100644 --- a/app/lib/src/features/organization/data/org_context_api_client.dart +++ b/app/lib/src/features/organization/data/org_context_api_client.dart @@ -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((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('|'); } } diff --git a/app/test/auth/auth_session_store_test.dart b/app/test/auth/auth_session_store_test.dart index 0f47c77..26fd87d 100644 --- a/app/test/auth/auth_session_store_test.dart +++ b/app/test/auth/auth_session_store_test.dart @@ -22,13 +22,6 @@ void main() { tenantName: 'Hanmac', tenantSlug: 'hanmac', ), - orgContextCredential: OrgContextCredential( - baseUrl: 'https://sadmin.hmac.kr', - tenantSlug: 'hanmac-family', - keyId: 'session-key-id', - keySecret: 'session-key-secret', - expiresAt: DateTime.parse('2099-07-02T13:00:00Z'), - ), ); await store.save(response); @@ -36,8 +29,6 @@ void main() { expect(loaded?.token, 'session-token'); expect(loaded?.user.name, 'User One'); - expect(loaded?.orgContextCredential?.keyId, 'session-key-id'); - expect(loaded?.orgContextCredential?.keySecret, 'session-key-secret'); await store.clear(); expect(await store.load(), isNull); diff --git a/app/test/organization/org_context_api_client_test.dart b/app/test/organization/org_context_api_client_test.dart index bd0cdb7..c65e536 100644 --- a/app/test/organization/org_context_api_client_test.dart +++ b/app/test/organization/org_context_api_client_test.dart @@ -22,14 +22,12 @@ void main() { expect(request.url.queryParameters['tenantSlug'], 'hanmac-family'); expect(request.url.queryParameters['includeUsers'], 'true'); expect(request.url.queryParameters['includeUserIds'], 'true'); - expect(request.headers['X-Baron-Key-ID'], 'key-id'); - expect(request.headers['X-Baron-Key-Secret'], 'key-secret'); + expect(request.headers.containsKey('X-Baron-Key-ID'), isFalse); + expect(request.headers.containsKey('X-Baron-Key-Secret'), isFalse); return _jsonResponse(_orgContextResponse()); }), baseUri: Uri.parse('https://sadmin.hmac.kr'), - keyId: 'key-id', - keySecret: 'key-secret', tenantSlug: 'hanmac-family', ); @@ -49,7 +47,7 @@ void main() { expect(is3.totalMemberCount, 2); }); - test('uses session org-context credential before env fallback', () async { + test('does not persist or send session org-context credentials', () async { const store = AuthSessionStore(); await store.save( PhoneLoginResponse( @@ -64,27 +62,19 @@ void main() { tenantName: '', tenantSlug: '', ), - orgContextCredential: const OrgContextCredential( - baseUrl: 'https://session.example.test', - tenantSlug: 'session-family', - keyId: 'session-key-id', - keySecret: 'session-key-secret', - ), ), ); final client = OrgContextApiClient( httpClient: MockClient((request) async { - expect(request.url.host, 'session.example.test'); - expect(request.url.queryParameters['tenantSlug'], 'session-family'); + expect(request.url.host, 'env.example.test'); + expect(request.url.queryParameters['tenantSlug'], 'env-family'); expect(request.headers['Authorization'], 'Bearer session-token'); - expect(request.headers['X-Baron-Key-ID'], 'session-key-id'); - expect(request.headers['X-Baron-Key-Secret'], 'session-key-secret'); + expect(request.headers.containsKey('X-Baron-Key-ID'), isFalse); + expect(request.headers.containsKey('X-Baron-Key-Secret'), isFalse); return _jsonResponse(_orgContextResponse()); }), baseUri: Uri.parse('https://env.example.test'), - keyId: 'env-key-id', - keySecret: 'env-key-secret', tenantSlug: 'env-family', sessionStore: store, ); @@ -99,8 +89,6 @@ void main() { return _jsonResponse(_orgContextResponse()); }), baseUri: Uri.parse('https://sadmin.hmac.kr'), - keyId: 'key-id', - keySecret: 'key-secret', tenantSlug: 'hanmac-family', ); @@ -115,8 +103,6 @@ void main() { return _jsonResponse(_orgContextResponse()); }), baseUri: Uri.parse('https://114-auth.hmac.kr'), - keyId: '', - keySecret: '', tenantSlug: 'hanmac-family', ); @@ -155,8 +141,6 @@ void main() { return _jsonResponse(_orgContextResponse()); }), baseUri: Uri.parse('https://114-auth.hmac.kr'), - keyId: '', - keySecret: '', tenantSlug: 'hanmac-family', sessionStore: store, ); @@ -173,8 +157,6 @@ void main() { return _jsonResponse(_orgContextResponse()); }), baseUri: Uri.parse('https://114-auth.hmac.kr'), - keyId: '', - keySecret: '', tenantSlug: 'hanmac-family', ); @@ -184,27 +166,22 @@ void main() { expect(requestCount, 1); }); - test( - 'falls back to env org-context credential without session credential', - () async { - final client = OrgContextApiClient( - httpClient: MockClient((request) async { - expect(request.url.host, 'env.example.test'); - expect(request.url.queryParameters['tenantSlug'], 'env-family'); - expect(request.headers['X-Baron-Key-ID'], 'env-key-id'); - expect(request.headers['X-Baron-Key-Secret'], 'env-key-secret'); - return _jsonResponse(_orgContextResponse()); - }), - baseUri: Uri.parse('https://env.example.test'), - keyId: 'env-key-id', - keySecret: 'env-key-secret', - tenantSlug: 'env-family', - sessionStore: const AuthSessionStore(), - ); + test('uses configured auth-server org-context endpoint without Baron key headers', () async { + final client = OrgContextApiClient( + httpClient: MockClient((request) async { + expect(request.url.host, 'env.example.test'); + expect(request.url.queryParameters['tenantSlug'], 'env-family'); + expect(request.headers.containsKey('X-Baron-Key-ID'), isFalse); + expect(request.headers.containsKey('X-Baron-Key-Secret'), isFalse); + return _jsonResponse(_orgContextResponse()); + }), + baseUri: Uri.parse('https://env.example.test'), + tenantSlug: 'env-family', + sessionStore: const AuthSessionStore(), + ); - await client.fetchOrgContext(); - }, - ); + await client.fetchOrgContext(); + }); test( 'remote directory repository filters org-context employees locally', @@ -215,8 +192,6 @@ void main() { return _jsonResponse(_orgContextResponse()); }), baseUri: Uri.parse('https://sadmin.hmac.kr'), - keyId: 'key-id', - keySecret: 'key-secret', tenantSlug: 'hanmac-family', ); final repository = RemoteDirectoryRepository(orgContextApiClient: client); @@ -238,8 +213,6 @@ void main() { return _jsonResponse(_orgContextResponse()); }), baseUri: Uri.parse('https://sadmin.hmac.kr'), - keyId: 'key-id', - keySecret: 'key-secret', tenantSlug: 'hanmac-family', ); final repository = RemoteDirectoryRepository(orgContextApiClient: client); @@ -264,8 +237,6 @@ void main() { return _jsonResponse(_orgContextResponse()); }), baseUri: Uri.parse('https://sadmin.hmac.kr'), - keyId: 'key-id', - keySecret: 'key-secret', tenantSlug: 'hanmac-family', ); final repository = RemoteDirectoryRepository(orgContextApiClient: client); diff --git a/docs/00_guide_tdc114plus_work_progress_timetable_2026-07-02.md b/docs/00_guide_tdc114plus_work_progress_timetable_2026-07-02.md index e415347..6d81e67 100644 --- a/docs/00_guide_tdc114plus_work_progress_timetable_2026-07-02.md +++ b/docs/00_guide_tdc114plus_work_progress_timetable_2026-07-02.md @@ -154,6 +154,7 @@ - 공기계 USB 연결 기준으로는 실데이터 화면까지 확인했지만, 현재 실행 모드는 `adb reverse + http://127.0.0.1:5000` 기반 로컬 Baron API 연결이므로 USB 분리 후 독립 동작은 아직 보장하지 않는다 - USB 없이 동작하는 실서버 APK 테스트로 가려면 `TDC114_API_BASE`를 staging 또는 production 공개 URL로 전환하고, headless 승인 로그인과 HTTPS 경로를 그 환경에서 다시 검증해야 한다 - 2026-07-08 기준 앱/스크립트는 `TDC114_AUTH_API_BASE`, `TDC114_DIRECTORY_API_BASE`, `TDC114_ORGANIZATION_API_BASE` 분리 주입을 지원하므로 `로그인은 staging`, `직원/조직 데이터는 production` 조합까지 실행 준비가 되어 있다 +- 2026-07-20 기준 앱에는 Baron org-context key와 NAVER WORKS secret을 주입하지 않는다. 해당 값은 `tdc114plus-auth` 서버 env에서만 관리하고, 앱은 중계서버 URL과 앱 세션 token만 사용한다 - 2026-07-10 기준 조직/직원 원본 참고 host는 staging `https://sadmin.hmac.kr`로 되돌린다. `admin.brsw.kr` production host는 현재 앱 개발 기준에서 우선 사용하지 않는다 - 2026-07-10 기준 Baron SSO 로그인 성공 시 조직도 API 호출용 ID/Secret을 함께 내려주는 기능은 아직 미개발로 보고, 앱은 해당 값을 받을 준비만 먼저 한다 - 해당 기능이 완성되기 전까지 조직/직원 데이터는 staging `org-context` endpoint와 로컬 비추적 env/Dart define의 고정 키 fallback으로 검증한다 diff --git a/scripts/README.md b/scripts/README.md index 4694482..e50d885 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -8,7 +8,7 @@ - `smoke.env.example`: authenticated smoke용 로컬 env 예시 파일이다. `scripts/.env.smoke.local`로 복사해 `TDC114_API_BASE`, `TDC114_SMOKE_PHONE`을 넣어 사용할 수 있다. - `smoke.staging.env.example`: staging Baron SSO 검증용 env 예시 파일이다. `scripts/.env.staging.local`로 복사해 `TDC114_API_BASE`, `TDC114_SMOKE_PHONE`과 optional expected label 값을 넣어 사용할 수 있다. - `android-device.env.example`: Android 공기계 USB 테스트용 env 예시 파일이다. `scripts/.env.android-device.local`로 복사해 `adb reverse` 기준 `TDC114_API_BASE=http://127.0.0.1:5000`, `TDC114_AUTH_API_BASE=http://127.0.0.1:5001`을 사용한다. -- `android-device.staging.env.example`: Android 공기계/실폰의 USB 없는 staging 직접 로그인 테스트용 env 예시 파일이다. `scripts/.env.android-device.staging.local`로 복사해 공개 staging HTTPS base URL을 넣어 사용한다. +- `android-device.staging.env.example`: Android 공기계/실폰의 USB 없는 staging 직접 로그인 테스트용 env 예시 파일이다. `scripts/.env.android-device.staging.local`로 복사해 공개 staging HTTPS base URL을 넣어 사용한다. Baron org-context key는 앱 env가 아니라 `tdc114plus-auth` 서버 env에만 둔다. - `android-device.production.env.example`: Android 공기계/실폰의 USB 없는 production 직접 로그인 테스트용 env 예시 파일이다. `scripts/.env.android-device.production.local`로 복사해 공개 production HTTPS base URL을 넣어 사용한다. - `bootstrap-baron-api-env.sh`: Baron SSO API worktree의 `.env.sample`을 바탕으로 로컬 smoke용 `.env`를 생성하고 localhost/알림 비활성 override를 덧붙인다. - `check-baron-api-env.sh`: Baron SSO API worktree의 `.env`, compose, config, Docker runtime 준비 상태를 점검해 실제 API smoke 가능 여부를 빠르게 확인한다. @@ -41,6 +41,7 @@ - ADB 로그 조회, `devices`, `reverse --list`, `screencap` 등 Flutter가 필요 없는 ADB 작업은 `flutter-docker.sh adb` 대신 `ADB_SERVER_SOCKET=tcp:172.21.128.1:5037 ./scripts/host-adb.sh ...`를 우선 사용한다. - USB 없는 staging 직접 로그인 검증 시에는 `TDC114_SMOKE_ENV_FILE=scripts/.env.android-device.staging.local`과 `TDC114_FLUTTER_DEVICE_ID=`를 명시한다. - 로그인만 staging, 직원/조직 정보만 production으로 분리해야 할 경우 `TDC114_AUTH_API_BASE`, `TDC114_DIRECTORY_API_BASE`, `TDC114_ORGANIZATION_API_BASE`를 추가로 지정한다. 값을 비우면 모두 `TDC114_API_BASE`를 따른다. +- 앱 실행 env에는 `TDC114_BARON_KEY_ID`, `TDC114_BARON_KEY_SECRET`, NAVER WORKS secret을 넣지 않는다. 조직도 key와 네이버웍스 key는 `tdc114plus-auth` 서버 환경변수에서만 관리한다. - `startup.sh`, `shutdown.sh`는 테스트나 다른 worktree 재사용을 위해 `BARON_SSO_WORKTREE`, `TDC114_LOG_BASE`, `DOCKER_BIN` 같은 환경 변수 override를 지원한다. - 업무 시작 절차에서는 앱 직접 App Link callback 테스트 서버를 더 이상 기동하지 않는다. 현재 로그인은 `tdc114plus-auth`의 `/api/v1/auth/link/init`, `/api/v1/auth/link/poll` 경로를 사용한다. - Windows Android Studio emulator + WSL/Docker Flutter 운영 기준은 `docs/policy_android_studio_wsl_adb_2026-07-03.md`를 따른다. diff --git a/scripts/android-device.staging.env.example b/scripts/android-device.staging.env.example index d1c6e94..3e55d77 100644 --- a/scripts/android-device.staging.env.example +++ b/scripts/android-device.staging.env.example @@ -3,10 +3,9 @@ # The app talks directly to the public Baron staging API over HTTPS. # No adb reverse is required in this mode. TDC114_API_BASE=https://sso.hmac.kr -TDC114_ORG_CONTEXT_API_BASE=https://sadmin.hmac.kr +TDC114_AUTH_API_BASE=https://114-auth.hmac.kr +TDC114_ORG_CONTEXT_API_BASE=https://114-auth.hmac.kr TDC114_ORG_CONTEXT_TENANT_SLUG=hanmac-family -TDC114_BARON_KEY_ID=replace-with-staging-key-id -TDC114_BARON_KEY_SECRET=replace-with-staging-key-secret # Skip local phone-login bootstrap and open the normal in-app login flow. TDC114_SKIP_SESSION_BOOTSTRAP=true @@ -18,7 +17,6 @@ TDC114_SMOKE_PHONE=010xxxxxxxx # TDC114_SMOKE_EXPECTED_TENANT_LABEL=IS3 # Optional split mode: -# Login and org-context stay on staging by default. -# TDC114_AUTH_API_BASE=https://sso.hmac.kr -# TDC114_DIRECTORY_API_BASE=https://sadmin.hmac.kr -# TDC114_ORGANIZATION_API_BASE=https://sadmin.hmac.kr +# Login and org-context should normally go through tdc114plus-auth. +# TDC114_DIRECTORY_API_BASE=https://114-auth.hmac.kr +# TDC114_ORGANIZATION_API_BASE=https://114-auth.hmac.kr diff --git a/scripts/integration_tests.sh b/scripts/integration_tests.sh index 129f839..83d2be1 100755 --- a/scripts/integration_tests.sh +++ b/scripts/integration_tests.sh @@ -49,8 +49,6 @@ append_optional_define "TDC114_DIRECTORY_API_BASE" append_optional_define "TDC114_ORGANIZATION_API_BASE" append_optional_define "TDC114_ORG_CONTEXT_API_BASE" append_optional_define "TDC114_ORG_CONTEXT_TENANT_SLUG" -append_optional_define "TDC114_BARON_KEY_ID" -append_optional_define "TDC114_BARON_KEY_SECRET" TEST_DEVICE_ID="${TDC114_FLUTTER_DEVICE_ID:-${TDC114_ADB_CONNECT_ADDRESS:-}}" if [ -n "$TEST_DEVICE_ID" ]; then diff --git a/scripts/manual-postlogin-run.sh b/scripts/manual-postlogin-run.sh index d4b8135..0afbe6f 100755 --- a/scripts/manual-postlogin-run.sh +++ b/scripts/manual-postlogin-run.sh @@ -56,8 +56,6 @@ append_optional_define "TDC114_DIRECTORY_API_BASE" append_optional_define "TDC114_ORGANIZATION_API_BASE" append_optional_define "TDC114_ORG_CONTEXT_API_BASE" append_optional_define "TDC114_ORG_CONTEXT_TENANT_SLUG" -append_optional_define "TDC114_BARON_KEY_ID" -append_optional_define "TDC114_BARON_KEY_SECRET" if [ "$SKIP_SESSION_BOOTSTRAP" = "1" ] || [ "$SKIP_SESSION_BOOTSTRAP" = "true" ]; then echo "Skipping session bootstrap; app will open its normal login flow." >&2 diff --git a/scripts/smoke.staging.env.example b/scripts/smoke.staging.env.example index 597677a..ef58c13 100644 --- a/scripts/smoke.staging.env.example +++ b/scripts/smoke.staging.env.example @@ -1,8 +1,7 @@ TDC114_API_BASE=https://sso.hmac.kr -TDC114_ORG_CONTEXT_API_BASE=https://sadmin.hmac.kr +TDC114_AUTH_API_BASE=https://114-auth.hmac.kr +TDC114_ORG_CONTEXT_API_BASE=https://114-auth.hmac.kr TDC114_ORG_CONTEXT_TENANT_SLUG=hanmac-family -TDC114_BARON_KEY_ID=replace-with-staging-key-id -TDC114_BARON_KEY_SECRET=replace-with-staging-key-secret TDC114_SMOKE_PHONE=010xxxxxxxx TDC114_SMOKE_AUTH_FLOW=link @@ -11,6 +10,5 @@ TDC114_SMOKE_AUTH_FLOW=link # TDC114_SMOKE_EXPECTED_TENANT_LABEL=IS3 # Optional split mode: -# TDC114_AUTH_API_BASE=https://sso.hmac.kr -# TDC114_DIRECTORY_API_BASE=https://sadmin.hmac.kr -# TDC114_ORGANIZATION_API_BASE=https://sadmin.hmac.kr +# TDC114_DIRECTORY_API_BASE=https://114-auth.hmac.kr +# TDC114_ORGANIZATION_API_BASE=https://114-auth.hmac.kr diff --git a/scripts/start-auth-server.sh b/scripts/start-auth-server.sh index 0d8c466..e4b91ee 100755 --- a/scripts/start-auth-server.sh +++ b/scripts/start-auth-server.sh @@ -107,14 +107,19 @@ if [ -f "$NAVER_WORKS_ENV_FILE" ]; then fi fi -: "${TDC114_ORG_CONTEXT_API_BASE:?TDC114_ORG_CONTEXT_API_BASE is required in $ENV_FILE}" -: "${TDC114_ORG_CONTEXT_TENANT_SLUG:?TDC114_ORG_CONTEXT_TENANT_SLUG is required in $ENV_FILE}" -: "${TDC114_BARON_KEY_ID:?TDC114_BARON_KEY_ID is required in $ENV_FILE}" -: "${TDC114_BARON_KEY_SECRET:?TDC114_BARON_KEY_SECRET is required in $ENV_FILE}" +ORG_CONTEXT_BASE="${BARON_ORG_CONTEXT_BASE_URL:-${TDC114_ORG_CONTEXT_API_BASE:-}}" +ORG_CONTEXT_TENANT_SLUG="${BARON_ORG_CONTEXT_TENANT_SLUG:-${TDC114_ORG_CONTEXT_TENANT_SLUG:-}}" +ORG_CONTEXT_KEY_ID="${BARON_ORG_CONTEXT_KEY_ID:-${TDC114_BARON_KEY_ID:-}}" +ORG_CONTEXT_KEY_SECRET="${BARON_ORG_CONTEXT_KEY_SECRET:-${TDC114_BARON_KEY_SECRET:-}}" + +: "${ORG_CONTEXT_BASE:?BARON_ORG_CONTEXT_BASE_URL is required in $ENV_FILE}" +: "${ORG_CONTEXT_TENANT_SLUG:?BARON_ORG_CONTEXT_TENANT_SLUG is required in $ENV_FILE}" +: "${ORG_CONTEXT_KEY_ID:?BARON_ORG_CONTEXT_KEY_ID is required in $ENV_FILE}" +: "${ORG_CONTEXT_KEY_SECRET:?BARON_ORG_CONTEXT_KEY_SECRET is required in $ENV_FILE}" UPSTREAM_ORG_CONTEXT_BASE="${TDC114_AUTH_UPSTREAM_ORG_CONTEXT_API_BASE:-}" if [ -z "$UPSTREAM_ORG_CONTEXT_BASE" ]; then - UPSTREAM_ORG_CONTEXT_BASE="$TDC114_ORG_CONTEXT_API_BASE" + UPSTREAM_ORG_CONTEXT_BASE="$ORG_CONTEXT_BASE" fi log "Starting tdc114plus-auth on :$PORT" @@ -138,9 +143,9 @@ log "Log file: $LOG_FILE" BARON_JWKS_KID="${BARON_JWKS_KID:-tdc114plus-auth-key-1}" \ BARON_LINK_RETURN_URI="${BARON_LINK_RETURN_URI:-}" \ BARON_ORG_CONTEXT_BASE_URL="$UPSTREAM_ORG_CONTEXT_BASE" \ - BARON_ORG_CONTEXT_TENANT_SLUG="$TDC114_ORG_CONTEXT_TENANT_SLUG" \ - BARON_ORG_CONTEXT_KEY_ID="$TDC114_BARON_KEY_ID" \ - BARON_ORG_CONTEXT_KEY_SECRET="$TDC114_BARON_KEY_SECRET" \ + BARON_ORG_CONTEXT_TENANT_SLUG="$ORG_CONTEXT_TENANT_SLUG" \ + BARON_ORG_CONTEXT_KEY_ID="$ORG_CONTEXT_KEY_ID" \ + BARON_ORG_CONTEXT_KEY_SECRET="$ORG_CONTEXT_KEY_SECRET" \ go run ./cmd/server >>"$LOG_FILE" 2>&1 < /dev/null & )