mirror of
https://github.com/immich-app/immich.git
synced 2024-12-29 15:11:58 +00:00
feat(mobile): Add support for Basic Authentication (#6840)
This commit is contained in:
parent
b4c211cad1
commit
5061c35c8d
14 changed files with 33 additions and 33 deletions
|
@ -49,7 +49,7 @@ class AlbumThumbnailListTile extends StatelessWidget {
|
|||
type: ThumbnailFormat.WEBP,
|
||||
),
|
||||
httpHeaders: {
|
||||
"Authorization": "Bearer ${Store.get(StoreKey.accessToken)}",
|
||||
"x-immich-user-token": Store.get(StoreKey.accessToken),
|
||||
},
|
||||
cacheKey: getAlbumThumbNailCacheKey(album, type: ThumbnailFormat.WEBP),
|
||||
errorWidget: (context, url, error) =>
|
||||
|
|
|
@ -78,8 +78,7 @@ class GalleryViewerPage extends HookConsumerWidget {
|
|||
final isPlayingMotionVideo = useState(false);
|
||||
final isPlayingVideo = useState(false);
|
||||
Offset? localPosition;
|
||||
final authToken = 'Bearer ${Store.get(StoreKey.accessToken)}';
|
||||
final header = {"Authorization": authToken};
|
||||
final header = {"x-immich-user-token": Store.get(StoreKey.accessToken)};
|
||||
final currentIndex = useState(initialIndex);
|
||||
final currentAsset = loadAsset(currentIndex.value);
|
||||
final isTrashEnabled =
|
||||
|
@ -524,8 +523,7 @@ class GalleryViewerPage extends HookConsumerWidget {
|
|||
imageUrl:
|
||||
'${Store.get(StoreKey.serverEndpoint)}/asset/thumbnail/$assetId',
|
||||
httpHeaders: {
|
||||
"Authorization":
|
||||
"Bearer ${Store.get(StoreKey.accessToken)}",
|
||||
"x-immich-user-token": Store.get(StoreKey.accessToken),
|
||||
},
|
||||
errorWidget: (context, url, error) =>
|
||||
const Icon(Icons.image_not_supported_outlined),
|
||||
|
|
|
@ -68,7 +68,7 @@ class VideoViewerPage extends HookConsumerWidget {
|
|||
children: [
|
||||
VideoPlayer(
|
||||
url: videoUrl,
|
||||
jwtToken: Store.get(StoreKey.accessToken),
|
||||
accessToken: Store.get(StoreKey.accessToken),
|
||||
isMotionVideo: isMotionVideo,
|
||||
onVideoEnded: onVideoEnded,
|
||||
onPaused: onPaused,
|
||||
|
@ -99,7 +99,7 @@ final _fileFamily =
|
|||
|
||||
class VideoPlayer extends StatefulWidget {
|
||||
final String? url;
|
||||
final String? jwtToken;
|
||||
final String? accessToken;
|
||||
final File? file;
|
||||
final bool isMotionVideo;
|
||||
final VoidCallback onVideoEnded;
|
||||
|
@ -114,7 +114,7 @@ class VideoPlayer extends StatefulWidget {
|
|||
const VideoPlayer({
|
||||
super.key,
|
||||
this.url,
|
||||
this.jwtToken,
|
||||
this.accessToken,
|
||||
this.file,
|
||||
required this.onVideoEnded,
|
||||
required this.isMotionVideo,
|
||||
|
@ -160,7 +160,7 @@ class _VideoPlayerState extends State<VideoPlayer> {
|
|||
videoPlayerController = widget.file == null
|
||||
? VideoPlayerController.networkUrl(
|
||||
Uri.parse(widget.url!),
|
||||
httpHeaders: {"Authorization": "Bearer ${widget.jwtToken}"},
|
||||
httpHeaders: {"x-immich-user-token": widget.accessToken ?? ""},
|
||||
)
|
||||
: VideoPlayerController.file(widget.file!);
|
||||
|
||||
|
|
|
@ -302,8 +302,7 @@ class BackupService {
|
|||
onProgress: ((bytes, totalBytes) =>
|
||||
uploadProgressCb(bytes, totalBytes)),
|
||||
);
|
||||
req.headers["Authorization"] =
|
||||
"Bearer ${Store.get(StoreKey.accessToken)}";
|
||||
req.headers["x-immich-user-token"] = Store.get(StoreKey.accessToken);
|
||||
req.headers["Transfer-Encoding"] = "chunked";
|
||||
|
||||
req.fields['deviceAssetId'] = entity.id;
|
||||
|
|
|
@ -89,8 +89,7 @@ class _AssetMarkerIcon extends StatelessWidget {
|
|||
imageUrl,
|
||||
cacheKey: cacheKey,
|
||||
headers: {
|
||||
"Authorization":
|
||||
"Bearer ${Store.get(StoreKey.accessToken)}",
|
||||
"x-immich-user-token": Store.get(StoreKey.accessToken),
|
||||
},
|
||||
errorListener: (_) =>
|
||||
const Icon(Icons.image_not_supported_outlined),
|
||||
|
|
|
@ -27,7 +27,7 @@ class MemoryCard extends StatelessWidget {
|
|||
super.key,
|
||||
});
|
||||
|
||||
String get authToken => 'Bearer ${Store.get(StoreKey.accessToken)}';
|
||||
String get accessToken => Store.get(StoreKey.accessToken);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -55,7 +55,7 @@ class MemoryCard extends StatelessWidget {
|
|||
cacheKey: getThumbnailCacheKey(
|
||||
asset,
|
||||
),
|
||||
headers: {"Authorization": authToken},
|
||||
headers: {"x-immich-user-token": accessToken},
|
||||
),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
|
|
|
@ -51,7 +51,7 @@ class CuratedPeopleRow extends StatelessWidget {
|
|||
itemBuilder: (context, index) {
|
||||
final person = content[index];
|
||||
final headers = {
|
||||
"Authorization": "Bearer ${Store.get(StoreKey.accessToken)}",
|
||||
"x-immich-user-token": Store.get(StoreKey.accessToken),
|
||||
};
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 18.0),
|
||||
|
|
|
@ -46,8 +46,7 @@ class ThumbnailWithInfo extends StatelessWidget {
|
|||
fit: BoxFit.cover,
|
||||
imageUrl: imageUrl!,
|
||||
httpHeaders: {
|
||||
"Authorization":
|
||||
"Bearer ${Store.get(StoreKey.accessToken)}",
|
||||
"x-immich-user-token": Store.get(StoreKey.accessToken),
|
||||
},
|
||||
errorWidget: (context, url, error) =>
|
||||
const Icon(Icons.image_not_supported_outlined),
|
||||
|
|
|
@ -123,8 +123,7 @@ class PersonResultPage extends HookConsumerWidget {
|
|||
backgroundImage: NetworkImage(
|
||||
getFaceThumbnailUrl(personId),
|
||||
headers: {
|
||||
"Authorization":
|
||||
"Bearer ${Store.get(StoreKey.accessToken)}",
|
||||
"x-immich-user-token": Store.get(StoreKey.accessToken),
|
||||
},
|
||||
),
|
||||
),
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
@ -106,6 +108,10 @@ class WebsocketNotifier extends StateNotifier<WebsocketState> {
|
|||
final accessToken = Store.get(StoreKey.accessToken);
|
||||
try {
|
||||
final endpoint = Uri.parse(Store.get(StoreKey.serverEndpoint));
|
||||
final headers = {"x-immich-user-token": accessToken};
|
||||
if (endpoint.userInfo.isNotEmpty) {
|
||||
headers["Authorization"] = "Basic ${base64.encode(utf8.encode(endpoint.userInfo))}";
|
||||
}
|
||||
|
||||
debugPrint("Attempting to connect to websocket");
|
||||
// Configure socket transports must be specified
|
||||
|
@ -118,7 +124,7 @@ class WebsocketNotifier extends StateNotifier<WebsocketState> {
|
|||
.enableForceNew()
|
||||
.enableForceNewConnection()
|
||||
.enableAutoConnect()
|
||||
.setExtraHeaders({"Authorization": "Bearer $accessToken"})
|
||||
.setExtraHeaders(headers)
|
||||
.build(),
|
||||
);
|
||||
|
||||
|
|
|
@ -31,12 +31,12 @@ class ApiService {
|
|||
setEndpoint(endpoint);
|
||||
}
|
||||
}
|
||||
String? _authToken;
|
||||
String? _accessToken;
|
||||
|
||||
setEndpoint(String endpoint) {
|
||||
_apiClient = ApiClient(basePath: endpoint);
|
||||
if (_authToken != null) {
|
||||
setAccessToken(_authToken!);
|
||||
if (_accessToken != null) {
|
||||
setAccessToken(_accessToken!);
|
||||
}
|
||||
userApi = UserApi(_apiClient);
|
||||
authenticationApi = AuthenticationApi(_apiClient);
|
||||
|
@ -134,8 +134,8 @@ class ApiService {
|
|||
}
|
||||
|
||||
setAccessToken(String accessToken) {
|
||||
_authToken = accessToken;
|
||||
_apiClient.addDefaultHeader('Authorization', 'Bearer $accessToken');
|
||||
_accessToken = accessToken;
|
||||
_apiClient.addDefaultHeader('x-immich-user-token', accessToken);
|
||||
}
|
||||
|
||||
ApiClient get apiClient => _apiClient;
|
||||
|
|
|
@ -95,11 +95,11 @@ class ImmichImage extends StatelessWidget {
|
|||
},
|
||||
);
|
||||
}
|
||||
final String? token = Store.get(StoreKey.accessToken);
|
||||
final String? accessToken = Store.get(StoreKey.accessToken);
|
||||
final String thumbnailRequestUrl = getThumbnailUrl(asset, type: type);
|
||||
return CachedNetworkImage(
|
||||
imageUrl: thumbnailRequestUrl,
|
||||
httpHeaders: {"Authorization": "Bearer $token"},
|
||||
httpHeaders: {"x-immich-user-token": accessToken ?? ""},
|
||||
cacheKey: getThumbnailCacheKey(asset, type: type),
|
||||
width: width,
|
||||
height: height,
|
||||
|
@ -177,7 +177,7 @@ class ImmichImage extends StatelessWidget {
|
|||
getThumbnailUrlForRemoteId(assetId, type: type),
|
||||
cacheKey: getThumbnailCacheKeyForRemoteId(assetId, type: type),
|
||||
headers: {
|
||||
"Authorization": 'Bearer ${Store.get(StoreKey.accessToken)}',
|
||||
"x-immich-user-token": Store.get(StoreKey.accessToken),
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -195,10 +195,10 @@ class ImmichImage extends StatelessWidget {
|
|||
context,
|
||||
);
|
||||
} else {
|
||||
final authToken = 'Bearer ${Store.get(StoreKey.accessToken)}';
|
||||
final accessToken = Store.get(StoreKey.accessToken);
|
||||
// Precache the remote image since we are not using local images
|
||||
return precacheImage(
|
||||
remoteThumbnailProvider(asset, type, {"Authorization": authToken}),
|
||||
remoteThumbnailProvider(asset, type, {"x-immich-user-token": accessToken}),
|
||||
context,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ Widget userAvatar(BuildContext context, User u, {double? radius}) {
|
|||
backgroundColor: context.primaryColor.withAlpha(50),
|
||||
foregroundImage: CachedNetworkImageProvider(
|
||||
url,
|
||||
headers: {"Authorization": "Bearer ${Store.get(StoreKey.accessToken)}"},
|
||||
headers: {"x-immich-user-token": Store.get(StoreKey.accessToken)},
|
||||
cacheKey: "user-${u.id}-profile",
|
||||
),
|
||||
// silence errors if user has no profile image, use initials as fallback
|
||||
|
|
|
@ -51,7 +51,7 @@ class UserCircleAvatar extends ConsumerWidget {
|
|||
placeholder: (_, __) => Image.memory(kTransparentImage),
|
||||
imageUrl: profileImageUrl,
|
||||
httpHeaders: {
|
||||
"Authorization": "Bearer ${Store.get(StoreKey.accessToken)}",
|
||||
"x-immich-user-token": Store.get(StoreKey.accessToken),
|
||||
},
|
||||
fadeInDuration: const Duration(milliseconds: 300),
|
||||
errorWidget: (context, error, stackTrace) => textIcon,
|
||||
|
|
Loading…
Reference in a new issue