Skip to content

Commit

Permalink
🛂 Basic authentication implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
LNA-DEV committed Oct 31, 2024
1 parent e464a89 commit 8ac80ab
Show file tree
Hide file tree
Showing 22 changed files with 456 additions and 20 deletions.
9 changes: 9 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@
<meta-data
android:name="flutterEmbedding"
android:value="2" />

<activity android:name="com.linusu.flutter_web_auth_2.CallbackActivity" android:exported="true">
<intent-filter android:label="flutter_web_auth_2">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="my.test.app" /> <!-- This must correspond to the custom scheme used for instantiatng the client... See below -->
</intent-filter>
</activity>
</application>

<uses-permission android:name="android.permission.INTERNET" />
Expand Down
9 changes: 9 additions & 0 deletions lib/apis/base_api.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import 'package:open_media_server_app/helpers/Preferences.dart';

class BaseApi {
static Map<String, String> getHeaders() {
return {
"Authorization": "Bearer ${Preferences.prefs?.getString("AccessToken")}"
};
}
}
26 changes: 21 additions & 5 deletions lib/apis/inventory_api.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'dart:convert';
import 'package:open_media_server_app/apis/base_api.dart';
import 'package:open_media_server_app/globals.dart';
import 'package:open_media_server_app/models/inventory/episode.dart';
import 'package:open_media_server_app/models/inventory/inventory_item.dart';
Expand All @@ -11,7 +12,10 @@ class InventoryApi {
Future<List<InventoryItem>> listItems(String category) async {
String apiUrl = "${Globals.BaseUrl}/api/inventory/items?";

var response = await http.get(Uri.parse("${apiUrl}category=$category"));
var headers = BaseApi.getHeaders();

var response = await http.get(Uri.parse("${apiUrl}category=$category"),
headers: headers);

if (response.statusCode == 200) {
List<dynamic> jsonResponse = json.decode(response.body);
Expand All @@ -24,7 +28,10 @@ class InventoryApi {
Future<Movie> getMovie(String id) async {
String apiUrl = "${Globals.BaseUrl}/api/inventory/movie?";

var response = await http.get(Uri.parse("${apiUrl}id=$id"));
var headers = BaseApi.getHeaders();

var response =
await http.get(Uri.parse("${apiUrl}id=$id"), headers: headers);

if (response.statusCode == 200) {
dynamic jsonResponse = json.decode(response.body);
Expand All @@ -37,7 +44,10 @@ class InventoryApi {
Future<Show> getShow(String id) async {
String apiUrl = "${Globals.BaseUrl}/api/inventory/show?";

var response = await http.get(Uri.parse("${apiUrl}id=$id"));
var headers = BaseApi.getHeaders();

var response =
await http.get(Uri.parse("${apiUrl}id=$id"), headers: headers);

if (response.statusCode == 200) {
dynamic jsonResponse = json.decode(response.body);
Expand All @@ -50,7 +60,10 @@ class InventoryApi {
Future<Season> getSeason(String id) async {
String apiUrl = "${Globals.BaseUrl}/api/inventory/season?";

var response = await http.get(Uri.parse("${apiUrl}id=$id"));
var headers = BaseApi.getHeaders();

var response =
await http.get(Uri.parse("${apiUrl}id=$id"), headers: headers);

if (response.statusCode == 200) {
dynamic jsonResponse = json.decode(response.body);
Expand All @@ -63,7 +76,10 @@ class InventoryApi {
Future<Episode> getEpisode(String id) async {
String apiUrl = "${Globals.BaseUrl}/api/inventory/episode?";

var response = await http.get(Uri.parse("${apiUrl}id=$id"));
var headers = BaseApi.getHeaders();

var response =
await http.get(Uri.parse("${apiUrl}id=$id"), headers: headers);

if (response.statusCode == 200) {
dynamic jsonResponse = json.decode(response.body);
Expand Down
10 changes: 7 additions & 3 deletions lib/apis/metadata_api.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import 'dart:convert';
import 'package:open_media_server_app/apis/base_api.dart';
import 'package:open_media_server_app/globals.dart';
import 'package:open_media_server_app/models/metadata/metadata_model.dart';
import 'package:http/http.dart' as http;

class MetadataApi {
Future<MetadataModel> getMetadata(String id, String category) async {
Future<MetadataModel> getMetadata(String id, String category) async {
String apiUrl = "${Globals.BaseUrl}/api/metadata?";

var response = await http.get(Uri.parse("${apiUrl}id=$id&category=$category"));
var headers = BaseApi.getHeaders();

var response = await http
.get(Uri.parse("${apiUrl}id=$id&category=$category"), headers: headers);

if (response.statusCode == 200) {
dynamic jsonResponse = json.decode(response.body);
Expand All @@ -16,4 +20,4 @@ class MetadataApi {
throw Exception('Failed to load metadata');
}
}
}
}
85 changes: 85 additions & 0 deletions lib/auth/login_manager.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// import 'package:fedodo_general/globals/preferences.dart';
// import 'package:fedodo_general/widgets/auth/oauth_handler/custom_web_base_dummy.dart'
// if (dart.library.html) '../oauth_handler/custom_web_base.dart';
import 'package:flutter/foundation.dart';
import 'package:oauth2_client/access_token_response.dart';
import 'package:oauth2_client/interfaces.dart';
import 'package:oauth2_client/oauth2_client.dart';
import 'package:open_media_server_app/globals.dart';
import 'package:open_media_server_app/helpers/Preferences.dart';
import 'package:random_string/random_string.dart';

class LoginManager {
late OAuth2Client client;

BaseWebAuth? baseWebAuth;

LoginManager() {
if (!Globals.isWeb) {
client = OAuth2Client(
authorizeUrl: Globals.AuthorizeUrl,
tokenUrl: Globals.TokenUrl,
redirectUri: "my.test.app:/oauth2redirect", // TODO
customUriScheme: "my.test.app",
);
} else {
// client = OAuth2Client(
// authorizeUrl:
// "https://auth.${Preferences.prefs!.getString("DomainName")}/oauth/authorize",
// tokenUrl:
// "https://auth.${Preferences.prefs!.getString("DomainName")}/oauth/token",
// redirectUri: AuthGlobals.redirectUriWeb,
// // refreshUrl: "https://auth.${GlobalSettings.domainName}/oauth/token",
// customUriScheme: Uri.parse(AuthGlobals.redirectUriWeb).authority,
// );
}

// if (kIsWeb) {
// baseWebAuth = CustomWebBase();
// }
}

Future<String?> login(String clientId, String clientSecret) async {
var state = Preferences.prefs?.getString("OAuth_State");
var codeVerifier = Preferences.prefs?.getString("OAuth_CodeVerifier");

if (kIsWeb && codeVerifier == null) {
codeVerifier = randomAlphaNumeric(80);
Preferences.prefs?.setString("OAuth_CodeVerifier", codeVerifier);
}

AccessTokenResponse tknResponse = await client.getTokenWithAuthCodeFlow(
clientId: clientId,
clientSecret: Uri.encodeQueryComponent(clientSecret),
scopes: ["offline_access"],
webAuthClient: baseWebAuth,
state: state,
codeVerifier: codeVerifier,
);

var refreshToken = tknResponse.refreshToken;
if (refreshToken != null) {
Preferences.prefs?.setString("RefreshToken", refreshToken);
}

Preferences.prefs?.setString("AccessToken", tknResponse.accessToken!);

return tknResponse.accessToken;
}

Future<String?> refreshAsync() async {
String clientId = Preferences.prefs!.getString("ClientId")!;
String clientSecret = Preferences.prefs!.getString("ClientSecret")!;

var tknResponse = await client.refreshToken(
Preferences.prefs!.getString("RefreshToken")!,
clientId: clientId,
clientSecret: Uri.encodeQueryComponent(clientSecret),
);

Preferences.prefs?.setString("AccessToken", tknResponse.accessToken!);
Preferences.prefs?.setString("RefreshToken", tknResponse.refreshToken!);

return tknResponse.accessToken;
}
}
4 changes: 2 additions & 2 deletions lib/gallery_item.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:open_media_server_app/globals.dart';
import 'package:open_media_server_app/models/internal/grid_item_model.dart';
import 'package:open_media_server_app/widgets/custom_image.dart';

class GridItem extends StatelessWidget {
final GridItemModel item;
Expand All @@ -16,7 +16,7 @@ class GridItem extends StatelessWidget {
Expanded(
child: ClipRRect(
borderRadius: BorderRadius.circular(8.0),
child: CachedNetworkImage(
child: CustomImage(
imageUrl: item.posterUrl ?? Globals.PictureNotFoundUrl,
fit: BoxFit.cover,
width: double.infinity,
Expand Down
8 changes: 8 additions & 0 deletions lib/globals.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
class Globals {
static String BaseUrl = "http://localhost:8080";

static String ClientId = "";
static String ClientSecret = "";
static String AuthorizeUrl = "";
static String TokenUrl = "";

static String Title = "Open Media Station";
static String PictureNotFoundUrl =
"https://static.vecteezy.com/system/resources/previews/005/337/799/original/icon-image-not-found-free-vector.jpg";

static bool isTv = false;
static bool isMobile = false;
static bool isWeb = false;
}
5 changes: 5 additions & 0 deletions lib/helpers/Preferences.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import 'package:shared_preferences/shared_preferences.dart';

class Preferences {
static SharedPreferences? prefs;
}
13 changes: 13 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:media_kit/media_kit.dart';
import 'package:open_media_server_app/auth/login_manager.dart';
import 'package:open_media_server_app/gallery.dart';
import 'package:open_media_server_app/globals.dart';
import 'package:open_media_server_app/helpers/Preferences.dart';
import 'package:shared_preferences/shared_preferences.dart';

Future main() async {
WidgetsFlutterBinding.ensureInitialized();
Expand All @@ -22,6 +25,16 @@ Future main() async {
Globals.isMobile = false;
}

if (kIsWeb) {
Globals.isWeb = true;
}

var prefs = await SharedPreferences.getInstance();
Preferences.prefs = prefs;

LoginManager loginManager = LoginManager();
var token = await loginManager.login(Globals.ClientId, Globals.ClientSecret);

runApp(const MyApp());
}

Expand Down
2 changes: 2 additions & 0 deletions lib/player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:media_kit/media_kit.dart';
import 'package:media_kit_video/media_kit_video.dart';
import 'package:open_media_server_app/apis/base_api.dart';
import 'package:open_media_server_app/player_controls/material_tv.dart';
import 'package:open_media_server_app/helpers/wrapper.dart';
import 'package:open_media_server_app/globals.dart';
Expand All @@ -27,6 +28,7 @@ class _PlayerState extends State<PlayerView> {
player.open(
Media(
widget.url,
httpHeaders: BaseApi.getHeaders(),
),
);

Expand Down
4 changes: 2 additions & 2 deletions lib/views/episode_detail.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:open_media_server_app/globals.dart';
import 'package:open_media_server_app/models/internal/grid_item_model.dart';
import 'package:open_media_server_app/player.dart';
import 'package:open_media_server_app/widgets/custom_image.dart';

class EpisodeDetailView extends StatelessWidget {
const EpisodeDetailView({
Expand Down Expand Up @@ -35,7 +35,7 @@ class EpisodeDetailView extends StatelessWidget {
Rect.fromLTRB(220, 220, rect.width, rect.height));
},
blendMode: BlendMode.dstIn,
child: CachedNetworkImage(
child: CustomImage(
imageUrl: itemModel.backdropUrl ?? Globals.PictureNotFoundUrl,
height: 300,
width: double.infinity,
Expand Down
4 changes: 2 additions & 2 deletions lib/views/movie_detail.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:open_media_server_app/globals.dart';
import 'package:open_media_server_app/models/internal/grid_item_model.dart';
import 'package:open_media_server_app/player.dart';
import 'package:open_media_server_app/widgets/custom_image.dart';

class MovieDetailView extends StatelessWidget {
const MovieDetailView({
Expand Down Expand Up @@ -36,7 +36,7 @@ class MovieDetailView extends StatelessWidget {
Rect.fromLTRB(220, 220, rect.width, rect.height));
},
blendMode: BlendMode.dstIn,
child: CachedNetworkImage(
child: CustomImage(
imageUrl: itemModel.backdropUrl ?? Globals.PictureNotFoundUrl,
height: 300,
width: double.infinity,
Expand Down
12 changes: 8 additions & 4 deletions lib/views/season_detail.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:open_media_server_app/apis/base_api.dart';
import 'package:open_media_server_app/apis/inventory_api.dart';
import 'package:open_media_server_app/apis/metadata_api.dart';
import 'package:open_media_server_app/globals.dart';
import 'package:open_media_server_app/models/internal/grid_item_model.dart';
import 'package:open_media_server_app/models/metadata/metadata_model.dart';
import 'package:open_media_server_app/views/episode_detail.dart';
import 'package:open_media_server_app/widgets/custom_image.dart';
import 'package:open_media_server_app/widgets/title.dart';

class SeasonDetailView extends StatelessWidget {
Expand Down Expand Up @@ -56,9 +58,9 @@ class SeasonDetailView extends StatelessWidget {
Rect.fromLTRB(220, 220, rect.width, rect.height));
},
blendMode: BlendMode.dstIn,
child: CachedNetworkImage(
imageUrl: itemModel.backdropUrl ??
Globals.PictureNotFoundUrl,
child: CustomImage(
imageUrl:
itemModel.backdropUrl ?? Globals.PictureNotFoundUrl,
height: 300,
width: double.infinity,
fit: BoxFit.cover,
Expand Down Expand Up @@ -114,14 +116,16 @@ class SeasonDetailView extends StatelessWidget {
image: CachedNetworkImageProvider(
element.backdropUrl ??
Globals.PictureNotFoundUrl,
headers: BaseApi.getHeaders(),
),
),
const SizedBox(
width: 16,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
element.metadataModel?.title != null
Expand Down
Loading

0 comments on commit 8ac80ab

Please sign in to comment.