Skip to content

chore: changes to demo apps auth flow #527

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions dogfooding/lib/app/app_content.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dogfooding/core/repos/token_service.dart';
import 'package:flutter_dogfooding/router/routes.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart' hide User;
import 'package:stream_video_flutter/stream_video_flutter.dart';
import 'package:uni_links/uni_links.dart';

Expand All @@ -31,8 +31,15 @@ Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
final credentials = prefs.userCredentials;
if (credentials == null) return;

final tokenResponse = await locator
.get<TokenService>()
.loadToken(userId: credentials.userInfo.id);

// Initialise the video client.
AppInjector.registerStreamVideo(User(info: credentials.userInfo));
AppInjector.registerStreamVideo(
tokenResponse,
User(info: credentials.userInfo),
);

// Handle the message.
await _handleRemoteMessage(message);
Expand Down Expand Up @@ -224,13 +231,7 @@ class _StreamDogFoodingAppContentState
routerConfig: _router,
theme: _buildTheme(Brightness.dark),
builder: (context, child) {
// Wrap the app in a StreamChat widget to provide it with the
// StreamChatClient instance.
return StreamChat(
client: locator.get(),
streamChatThemeData: StreamChatThemeData.dark(),
child: child!,
);
return child!;
},
);
}
Expand Down
17 changes: 15 additions & 2 deletions dogfooding/lib/app/user_auth_controller.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
// 🐦 Flutter imports:
import 'package:flutter/cupertino.dart';
import 'package:flutter_dogfooding/core/repos/token_service.dart';
import 'package:flutter_dogfooding/core/repos/user_chat_repository.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart' hide User;

// 📦 Package imports:
import 'package:stream_video_flutter/stream_video_flutter.dart';
Expand All @@ -26,9 +29,13 @@ import '../di/injector.dart';
class UserAuthController extends ChangeNotifier {
UserAuthController({
required AppPreferences prefs,
}) : _prefs = prefs;
required TokenService tokenService,
}) : _prefs = prefs,
_tokenService = tokenService;

final AppPreferences _prefs;
final TokenService _tokenService;

UserAuthRepository? _authRepo;

/// Returns the current user if they are logged in, or null if they are not.
Expand All @@ -37,7 +44,10 @@ class UserAuthController extends ChangeNotifier {

/// Logs in the given [user] and returns the user credentials.
Future<UserCredentials> login(User user) async {
_authRepo ??= locator.get<UserAuthRepository>(param1: user);
final tokenResponse = await _tokenService.loadToken(userId: user.id);

_authRepo ??=
locator.get<UserAuthRepository>(param1: user, param2: tokenResponse);
final credentials = await _authRepo!.login();
_currentUser = credentials.userInfo;

Expand All @@ -62,6 +72,9 @@ class UserAuthController extends ChangeNotifier {
locator.unregister<StreamVideo>(
disposingFunction: (_) => StreamVideo.reset(),
);

locator.unregister<StreamChatClient>();
locator.unregister<UserChatRepository>();
}

await _prefs.clearUserCredentials();
Expand Down
22 changes: 15 additions & 7 deletions dogfooding/lib/core/repos/token_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,25 @@ import 'dart:convert';
// 📦 Package imports:
import 'package:http/http.dart' as http;

class TokenService {
const TokenService({required this.apiKey});

class TokenResponse {
final String token;
final String apiKey;

Future<String> loadToken({
const TokenResponse(this.token, this.apiKey);

factory TokenResponse.fromJson(Map<String, dynamic> json) =>
TokenResponse(json['token'], json['apiKey']);
}

class TokenService {
const TokenService();

Future<TokenResponse> loadToken({
required String userId,
Duration? expiresIn,
}) async {
final queryParameters = <String, dynamic>{
'api_key': apiKey,
'environment': 'pronto',
'user_id': userId,
};
if (expiresIn != null) {
Expand All @@ -23,13 +31,13 @@ class TokenService {

final uri = Uri(
scheme: 'https',
host: 'stream-calls-dogfood.vercel.app',
host: 'pronto.getstream.io',
path: '/api/auth/create-token',
queryParameters: queryParameters,
);

final response = await http.get(uri);
final body = json.decode(response.body) as Map<String, dynamic>;
return body['token'];
return TokenResponse.fromJson(body);
}
}
4 changes: 3 additions & 1 deletion dogfooding/lib/core/repos/user_chat_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ class UserChatRepository {
Future<OwnUser> connectUser(User user) {
return chatClient.connectUserWithProvider(
user,
(userId) => tokenService.loadToken(userId: userId),
(userId) => tokenService
.loadToken(userId: userId)
.then((response) => response.token),
);
}

Expand Down
55 changes: 35 additions & 20 deletions dogfooding/lib/di/injector.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import 'package:stream_video_push_notification/stream_video_push_notification.da
import '../app/user_auth_controller.dart';
import '../core/repos/token_service.dart';
import '../core/repos/user_auth_repository.dart';
import '../env/env.dart';
import '../log_config.dart';
import '../utils/consts.dart';

Expand All @@ -32,25 +31,19 @@ class AppInjector {

// App Preferences
final prefs = await SharedPreferences.getInstance();
locator.registerSingleton<AppPreferences>(AppPreferences(prefs: prefs));

// Stream chat
locator.registerLazySingleton<StreamChatClient>(_initStreamChat);
locator.registerSingleton<AppPreferences>(AppPreferences(prefs: prefs));

// Repositories
locator.registerSingleton(const TokenService(apiKey: Env.apiKey));
locator.registerLazySingleton<UserChatRepository>(
() => UserChatRepository(
chatClient: locator(),
tokenService: locator(),
),
);
locator.registerSingleton(const TokenService());

locator.registerFactoryParam<UserAuthRepository, User, TokenResponse>(
(user, tokenResponse) {
registerStreamChat(tokenResponse.apiKey);

locator.registerFactoryParam<UserAuthRepository, User, void>(
(user, _) {
// We need to register the video client here because we need it to
// initialise the user auth repo.
registerStreamVideo(user);
registerStreamVideo(tokenResponse, user);

return UserAuthRepository(
videoClient: locator(),
Expand All @@ -62,20 +55,40 @@ class AppInjector {
// App wide Controller
locator.registerLazySingleton<UserAuthController>(
dispose: (controller) => controller.dispose(),
() => UserAuthController(prefs: locator()),
() => UserAuthController(prefs: locator(), tokenService: locator()),
);
}

static void registerStreamChat(String apiKey) {
locator.registerSingleton<StreamChatClient>(
_initStreamChat(apiKey),
dispose: (client) => client.dispose(),
);

locator.registerLazySingleton<UserChatRepository>(
() => UserChatRepository(
chatClient: locator(),
tokenService: locator(),
),
);
}

static StreamVideo registerStreamVideo(User user) {
static StreamVideo registerStreamVideo(
TokenResponse tokenResponse, User user) {
_setupLogger();

return locator.registerSingleton(
dispose: (_) => StreamVideo.reset(),
_initStreamVideo(
tokenResponse.apiKey,
user,
initialToken: tokenResponse.token,
tokenLoader: switch (user.type) {
UserType.authenticated => (String userId) {
final tokenService = locator<TokenService>();
return tokenService.loadToken(userId: userId);
return tokenService
.loadToken(userId: userId)
.then((response) => response.token);
},
_ => null,
},
Expand Down Expand Up @@ -112,21 +125,23 @@ StreamLog _setupLogger() {
return StreamLog()..logger = CompositeStreamLogger(children);
}

StreamChatClient _initStreamChat() {
StreamChatClient _initStreamChat(String apiKey) {
final streamChatClient = StreamChatClient(
Env.apiKey,
apiKey,
logLevel: Level.INFO,
);

return streamChatClient;
}

StreamVideo _initStreamVideo(
String apiKey,
User user, {
String? initialToken,
TokenLoader? tokenLoader,
}) {
final streamVideoClient = StreamVideo(
Env.apiKey,
apiKey,
user: user,
tokenLoader: tokenLoader,
options: const StreamVideoOptions(
Expand Down
12 changes: 0 additions & 12 deletions dogfooding/lib/env/env.dart

This file was deleted.

20 changes: 19 additions & 1 deletion dogfooding/lib/router/router.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
// 📦 Package imports:
import 'package:flutter_dogfooding/di/injector.dart';
import 'package:go_router/go_router.dart';

// 🌎 Project imports:
import 'package:flutter_dogfooding/router/routes.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import '../app/user_auth_controller.dart';

GoRouter initRouter(UserAuthController authNotifier) {
return GoRouter(
routes: $appRoutes,
routes: [
ShellRoute(
routes: [
$homeRoute,
$lobbyRoute,
$callRoute,
],
builder: (context, state, child) {
return StreamChat(
client: locator.get(),
streamChatThemeData: StreamChatThemeData.dark(),
child: child,
);
},
),
$loginRoute,
],
refreshListenable: authNotifier,
redirect: (context, state) {
// get the current user
Expand Down
3 changes: 2 additions & 1 deletion dogfooding/lib/utils/loading_dialog.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// 🐦 Flutter imports:
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

// 📦 Package imports:
import 'package:stream_video_flutter/stream_video_flutter.dart';
Expand All @@ -24,4 +25,4 @@ Future<void> showLoadingIndicator(BuildContext context) async {
);
}

void hideLoadingIndicator(BuildContext context) => Navigator.of(context).pop();
void hideLoadingIndicator(BuildContext context) => context.pop();
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ android {
applicationId "com.example.example"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
minSdkVersion Math.max(flutter.minSdkVersion, 23)
minSdkVersion Math.max(flutter.minSdkVersion, 24)
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
Expand Down
2 changes: 1 addition & 1 deletion packages/stream_video_flutter/example/ios/Podfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '11.0'
platform :ios, '12.1'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
Expand Down
Loading