Skip to content
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

feat(mobile): Added support for BasicAuth via userinfo in server url #6617

Closed
wants to merge 1 commit into from

Conversation

jpg0
Copy link

@jpg0 jpg0 commented Jan 24, 2024

Added support for BasicAuth protected immich servers to mobile apps.

Allows specifying credentials in https://user:pass@host format in server URL. They will be sent in the Authorization header for all requests - in this case moving the bearer token auth to the (already supported) x-immich-user-token (it's not moved when BasicAuth is not used). I have added a note in the docs, and tested using Traefik.

@jpg0 jpg0 changed the title Added support for BasicAuth via userinfo in server url feat(mobile) Added support for BasicAuth via userinfo in server url Jan 24, 2024
@jpg0 jpg0 changed the title feat(mobile) Added support for BasicAuth via userinfo in server url feat(mobile): Added support for BasicAuth via userinfo in server url Jan 24, 2024
@alextran1502
Copy link
Contributor

what is the use case of this?

@jpg0
Copy link
Author

jpg0 commented Jan 24, 2024 via email

@jpg0
Copy link
Author

jpg0 commented Jan 24, 2024

Existing discussion: #3204

@alextran1502
Copy link
Contributor

So it is currently working with the web app but not the mobile app, correct?

@jpg0
Copy link
Author

jpg0 commented Jan 26, 2024

Yes. With the web app the browser handles this automatically.

@rovo89
Copy link
Contributor

rovo89 commented Jan 29, 2024

I would really appreciate if Basic Auth was working. I have various services running on my home server, exposed via nginx with mTLS. This way, I have an extra layer of protection in case of application bugs (e.g. forgotten authorization check for an endpoint), because attackers would additionally need my certificate (or a 0-day in nginx). Otherwise none of the resources are reachable.

There's a discussion saying that mTLS will probably never be supported in the app because the underlying framework doesn't support it. Fair enough.

My next attempt was Basic Auth, but while the initial connection attempt (like fetching server config) used the username/password, further requests added the Bearer token and that overwrote Basic Auth.

I currently use a separate domain with a secret path prefix (like https://app.photos.example.org/xyz). I disabled mTLS for that domain and forward only /xyz to Immich. Since the path is only transmitted within TLS, that should be save (as long as it's a bit more complex than xyz). But obviously, it's a hack and also doesn't work in the browser (which requires Immich to be served from top level).

So I'll gladly test this PR with nginx. Is there any automatically built APK?

@jpg0
Copy link
Author

jpg0 commented Jan 31, 2024

I don't believe that there is an official APK built automatically (well, certainly not until the workflows are run), but I have the one that I have built and am now using. It is not using the official signing key however (as I don't have that), so you'd need to uninstall the official Immich before installing this one.

It's too big to attach here, but you can get it from my Google drive: https://drive.google.com/file/d/14t3WGoRfSqpSz7-M8JsC2sm8M2ucPf1r/view?usp=sharing

@bo0tzz
Copy link
Member

bo0tzz commented Jan 31, 2024

the initial connection attempt (like fetching server config) used the username/password, further requests added the Bearer token and that overwrote Basic Auth.

This sounds to me like basic auth already 'just works', except we end up overwriting the auth header with the Immich token later on. If that is the case, it should be sufficient to just always put the Immich token in x-immich-user-token (a simple one line change) without needing any sort of "does this URL contain basic auth stuff" checks.

@jpg0
Copy link
Author

jpg0 commented Jan 31, 2024

This sounds to me like basic auth already 'just works',

TBH I'm a little confused at this because it certainly didn't for me. There is some fallback mechanism in the app which allows you to proceed to requesting username/password even if it cannot access the server info, but there is no BasicAuth headers actually being sent to the server.

As you can see in the commit for this PR, the userInfo data is explicitly pulled from the URI and attached as BasicAuth. (Actually this is pretty much all that the change actually does.)

@bo0tzz
Copy link
Member

bo0tzz commented Jan 31, 2024

there is no BasicAuth headers actually being sent to the server.

Then I misunderstood. I expected that whatever http client we're using already supported basic auth under the hood but we were just overwriting the headers.

@rovo89
Copy link
Contributor

rovo89 commented Jan 31, 2024

From my nginx logs:

192.168.2.222 - x [15/Jan/2024:17:03:45 +0100] "GET /api HTTP/1.1" 404 66 "-" "Dart/3.1 (dart:io)" "-"
192.168.2.222 - x [15/Jan/2024:17:03:45 +0100] "GET /.well-known/immich HTTP/1.1" 200 27 "-" "Dart/3.1 (dart:io)" "-"
192.168.2.222 - - [15/Jan/2024:17:03:45 +0100] "GET /api/server-info/version HTTP/1.1" 200 32 "-" "Dart/3.1 (dart:io)" "-"
192.168.2.222 - - [15/Jan/2024:17:03:45 +0100] "GET /api/server-info/features HTTP/1.1" 200 199 "-" "Dart/3.1 (dart:io)" "-"
192.168.2.222 - - [15/Jan/2024:17:03:45 +0100] "GET /api/server-info/config HTTP/1.1" 200 162 "-" "Dart/3.1 (dart:io)" "-"

In that example I used something like https://x:y@example.org as server URL in the Android app.
Maybe the credentials get lost when constructing the API URL from the well-known manifest? Not sure if the server version requires authentication and if the token would already be sent on that request. We might have two (small?) issues preventing Basic Auth.

@jpg0
Copy link
Author

jpg0 commented Jan 31, 2024

All the URLs in your log don't require authentication. Also if you set a URL with a user:pass at the front then typically the app just throws them away, so what you are seeing makes sense to me for a typical Immich install.

However I thought that you would be routing everything through a reverse-proxy and requiring basic auth on all requests - if so then I don't understand what is going on there as I don't believe that the basic auth headers would be sent.

Comment on lines +29 to +38
if(uri.userInfo.contains(":")) { //need BasicAuth, use custom header for Bearer Auth
headers['Authorization'] = "Basic ${base64.encode(utf8.encode(uri.userInfo))}";

if(accessToken != null) {
headers['x-immich-user-token'] = accessToken;
}

} else if(accessToken != null) {
headers['Authorization'] = 'Bearer $accessToken';
}
Copy link
Contributor

@jrasm91 jrasm91 Jan 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not simply always add the access token to the custom header instead?

@rovo89
Copy link
Contributor

rovo89 commented Feb 1, 2024

I pushed an alternative implementation in #6840 which "simply" changes all headers from Authorization: Bearer to x-immich-user-token (in more than a dozen places...). Please take a look @jpg0 @jrasm91.

@jpg0
Copy link
Author

jpg0 commented Feb 2, 2024

Looks like i missed a few places! I'm certainly happy with switching the token header entirely - I only left it as-is for non Basic to try to change as little as possible, but I agree a single approach is better.

Only one thing I have a question on which is the client sending basic auth headers - i'll comment on your PR though.

@alextran1502
Copy link
Contributor

superseded by #6840

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants