feat(server): accept the iOS app origin for mobile passkeys (#32733)

(cherry picked from commit 1119e9c7a7c9f5ad657aa4e893b897772f75a124)
This commit is contained in:
Santo Shakil
2026-10-27 23:11:30 +03:00
committed by Codex
parent a2794f0986
commit 6569aad605
2 changed files with 6 additions and 2 deletions
+2 -1
View File
@@ -308,7 +308,7 @@ describe(PasskeyService.name, () => {
expect(mocks.passkey.create).not.toHaveBeenCalled();
});
it('should verify against the android app origins when the origin header is missing', async () => {
it('should verify against the mobile app origins when the origin header is missing', async () => {
const auth = AuthFactory.from().session().build();
const passkey = PasskeyFactory.create({ userId: auth.user.id });
@@ -323,6 +323,7 @@ describe(PasskeyService.name, () => {
expect(mocks.webAuthn.verifyRegistrationResponse).toHaveBeenCalledWith(
expect.objectContaining({
expectedOrigin: [
'https://my.immich.app',
'android:apk-key-hash:hsXEVd-vSYWSOo81rbMdDJ4LlX1_lMLSr2okOKqWACA',
'android:apk-key-hash:WiLBg0dUBfVJxOufsmwuk6PvnFdmFQp684yNP-UVzNY',
],
+4 -1
View File
@@ -32,6 +32,9 @@ const CHALLENGE_TTL = Duration.fromObject({ minutes: 5 });
const MOBILE_RP_ID = 'my.immich.app';
// iOS apps send https:// + the rp id as their origin
const IOS_ORIGIN = `https://${MOBILE_RP_ID}`;
// keep in sync with https://my.immich.app/.well-known/assetlinks.json
const ANDROID_CERTIFICATE_FINGERPRINTS = [
'86:C5:C4:55:DF:AF:49:85:92:3A:8F:35:AD:B3:1D:0C:9E:0B:95:7D:7F:94:C2:D2:AF:6A:24:38:AA:96:00:20',
@@ -273,7 +276,7 @@ export class PasskeyService extends BaseService {
const origin = headers.origin;
// no origin implies a mobile app
if (!origin) {
return { origin: ANDROID_ORIGINS, rpId: MOBILE_RP_ID };
return { origin: [IOS_ORIGIN, ...ANDROID_ORIGINS], rpId: MOBILE_RP_ID };
}
let hostname: string;