import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:goatagent/firebase.dart'; import 'package:goatagent/screens/auth.dart'; import 'package:oauth2/oauth2.dart' as oauth2; class AuthGuard { static final AuthGuard _singleton = AuthGuard._internal(); final deviceEndpoint = Uri.parse('https://id.smartsheep.studio/api/notifications/subscribe'); final authorizationEndpoint = Uri.parse('https://id.smartsheep.studio/auth/o/connect'); final tokenEndpoint = Uri.parse('https://id.smartsheep.studio/api/auth/token'); final userinfoEndpoint = Uri.parse('https://id.smartsheep.studio/api/users/me'); final redirectUrl = Uri.parse('goatagent://auth'); static const clientId = "goatagent"; static const clientSecret = "_F4%q2Eea3"; static const storage = FlutterSecureStorage(); static const storageKey = "identity"; static const profileKey = "profiles"; factory AuthGuard() { return _singleton; } oauth2.Client? client; Future pickClient() async { if (await storage.containsKey(key: storageKey)) { try { var credentials = oauth2.Credentials.fromJson((await storage.read(key: storageKey))!); client = oauth2.Client(credentials, identifier: clientId, secret: clientSecret); await pullProfiles(); return true; } catch (e) { logout(); return false; } } else { return false; } } Future createClient(BuildContext context) async { // If logged in if (await pickClient()) { return client!; } var grant = oauth2.AuthorizationCodeGrant( clientId, authorizationEndpoint, tokenEndpoint, secret: clientSecret, basicAuth: false, ); var authorizationUrl = grant.getAuthorizationUrl(redirectUrl); if (Platform.isAndroid || Platform.isIOS) { // Let Goatpass know it is embed in an app authorizationUrl = authorizationUrl.replace( queryParameters: {"embedded": "yes"} ..addAll(authorizationUrl.queryParameters)); // Use WebView to get authorization url var responseUrl = await Navigator.of(context, rootNavigator: true).push( MaterialPageRoute( builder: (context) => AuthorizationScreen(authorizationUrl), ), ); var responseUri = Uri.parse(responseUrl); return await grant .handleAuthorizationResponse(responseUri.queryParameters); } else { throw UnimplementedError("unsupported platform"); } } Future pullProfiles() async { if (client != null) { var userinfo = await client!.get(userinfoEndpoint); storage.write(key: profileKey, value: utf8.decode(userinfo.bodyBytes)); } } Future login(BuildContext context) async { try { client = await createClient(context); storage.write(key: storageKey, value: client!.credentials.toJson()); await pullProfiles(); await subscribeNotify(); } catch (e) { print(e); } } Future subscribeNotify() async { if (client == null) { return; } var token = await initializeFirebaseMessaging(); if (token == null) { print("failed to initialize firebase messaging..."); return; } var response = await client!.post( deviceEndpoint, headers: {"Content-Type": "application/json"}, body: jsonEncode({"device_id": token, "provider": "firebase"}), ); if (response.statusCode != 200) { print(response.body); } } void logout() { try { storage.delete(key: profileKey); storage.delete(key: storageKey); } catch (e) { print(e); } } Future isAuthorized() async { const storage = FlutterSecureStorage(); return await storage.containsKey(key: storageKey); } Future readProfiles() async { const storage = FlutterSecureStorage(); return jsonDecode(await storage.read(key: profileKey) ?? "{}"); } AuthGuard._internal(); }