Skip to content

Commit

Permalink
feat(server,web): OIDC Implementation (#884)
Browse files Browse the repository at this point in the history
* chore: merge

* feat: nullable password

* feat: server debugger

* chore: regenerate api

* feat: auto-register flag

* refactor: oauth endpoints

* chore: regenerate api

* fix: default scope configuration

* refactor: pass in redirect uri from client

* chore: docs

* fix: bugs

* refactor: auth services and user repository

* fix: select password

* fix: tests

* fix: get signing algorithm from discovery document

* refactor: cookie constants

* feat: oauth logout

* test: auth services

* fix: query param check

* fix: regenerate open-api
  • Loading branch information
jrasm91 committed Nov 15, 2022
1 parent d476656 commit d3c35ec
Show file tree
Hide file tree
Showing 51 changed files with 1,994 additions and 250 deletions.
68 changes: 68 additions & 0 deletions docs/docs/usage/oauth.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---
sidebar_position: 5
---

# OAuth Authentication

This page contains details about using OAuth 2 in Immich.

## Overview

Immich supports 3rd party authentication via [OpenID Connect][oidc] (OIDC), an identity layer built on top of OAuth2. OIDC is supported by most identity providers, including:

- [Authentik](https://goauthentik.io/integrations/sources/oauth/#openid-connect)
- [Authelia](https://www.authelia.com/configuration/identity-providers/open-id-connect/)
- [Okta](https://www.okta.com/openid-connect/)
- [Google](https://developers.google.com/identity/openid-connect/openid-connect)

## Prerequisites

Before enabling OAuth in Immich, a new client application needs to be configured in the 3rd-party authentication server. While the specifics of this setup vary from provider to provider, the general approach should be the same.

1. Create a new (Client) Application

1. The **Provider** type should be `OpenID Connect` or `OAuth2`
2. The **Client type** should be `Confidential`
3. The **Application** type should be `Web`
4. The **Grant** type should be `Authorization Code`

2. Configure Redirect URIs/Origins

1. The **Sign-in redirect URIs** should include:

- All URLs that will be used to access the login page of the Immich web client (eg. `http://localhost:2283/auth/login`, `http://192.168.0.200:2283/auth/login`, `https://immich.example.com/auth/login`)

## Enable OAuth

Once you have a new OAuth client application configured, Immich can be configured using the following environment variables:

| Key | Type | Default | Description |
| ------------------- | ------- | -------------------- | ------------------------------------------------------------------------- |
| OAUTH_ENABLED | boolean | false | Enable/disable OAuth2 |
| OAUTH_ISSUER_URL | URL | (required) | Required. Self-discovery URL for client (from previous step) |
| OAUTH_CLIENT_ID | string | (required) | Required. Client ID (from previous step) |
| OAUTH_CLIENT_SECRET | string | (required) | Required. Client Secret (previous step |
| OAUTH_SCOPE | string | openid email profile | Full list of scopes to send with the request (space delimited) |
| OAUTH_AUTO_REGISTER | boolean | true | When true, will automatically register a user the first time they sign in |
| OAUTH_BUTTON_TEXT | string | Login with OAuth | Text for the OAuth button on the web |

:::info
The Issuer URL should look something like the following, and return a valid json document.

- `https://accounts.google.com/.well-known/openid-configuration`
- `http://localhost:9000/application/o/immich/.well-known/openid-configuration`

The `.well-known/openid-configuration` part of the url is optional and will be automatically added during discovery.
:::

Here is an example of a valid configuration for setting up Immich to use OAuth with Authentik:

```
OAUTH_ENABLED=true
OAUTH_ISSUER_URL=http://192.168.0.187:9000/application/o/immich
OAUTH_CLIENT_ID=f08f9c5b4f77dcfd3916b1c032336b5544a7b368
OAUTH_CLIENT_SECRET=6fe2e697644da6ff6aef73387a457d819018189086fa54b151a6067fbb884e75f7e5c90be16d3c688cf902c6974817a85eab93007d76675041eaead8c39cf5a2
OAUTH_BUTTON_TEXT=Login with Authentik
```

[oidc]: https://openid.net/connect/
8 changes: 8 additions & 0 deletions mobile/openapi/.openapi-generator/FILES
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ doc/JobStatusResponseDto.md
doc/LoginCredentialDto.md
doc/LoginResponseDto.md
doc/LogoutResponseDto.md
doc/OAuthApi.md
doc/OAuthCallbackDto.md
doc/OAuthConfigDto.md
doc/OAuthConfigResponseDto.md
doc/RemoveAssetsDto.md
doc/SearchAssetDto.md
doc/ServerInfoApi.md
Expand Down Expand Up @@ -73,6 +77,7 @@ lib/api/asset_api.dart
lib/api/authentication_api.dart
lib/api/device_info_api.dart
lib/api/job_api.dart
lib/api/o_auth_api.dart
lib/api/server_info_api.dart
lib/api/user_api.dart
lib/api_client.dart
Expand Down Expand Up @@ -122,6 +127,9 @@ lib/model/job_status_response_dto.dart
lib/model/login_credential_dto.dart
lib/model/login_response_dto.dart
lib/model/logout_response_dto.dart
lib/model/o_auth_callback_dto.dart
lib/model/o_auth_config_dto.dart
lib/model/o_auth_config_response_dto.dart
lib/model/remove_assets_dto.dart
lib/model/search_asset_dto.dart
lib/model/server_info_response_dto.dart
Expand Down
5 changes: 5 additions & 0 deletions mobile/openapi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ Class | Method | HTTP request | Description
*JobApi* | [**getAllJobsStatus**](doc//JobApi.md#getalljobsstatus) | **GET** /jobs |
*JobApi* | [**getJobStatus**](doc//JobApi.md#getjobstatus) | **GET** /jobs/{jobId} |
*JobApi* | [**sendJobCommand**](doc//JobApi.md#sendjobcommand) | **PUT** /jobs/{jobId} |
*OAuthApi* | [**callback**](doc//OAuthApi.md#callback) | **POST** /oauth/callback |
*OAuthApi* | [**generateConfig**](doc//OAuthApi.md#generateconfig) | **POST** /oauth/config |
*ServerInfoApi* | [**getServerInfo**](doc//ServerInfoApi.md#getserverinfo) | **GET** /server-info |
*ServerInfoApi* | [**getServerVersion**](doc//ServerInfoApi.md#getserverversion) | **GET** /server-info/version |
*ServerInfoApi* | [**getStats**](doc//ServerInfoApi.md#getstats) | **GET** /server-info/stats |
Expand Down Expand Up @@ -160,6 +162,9 @@ Class | Method | HTTP request | Description
- [LoginCredentialDto](doc//LoginCredentialDto.md)
- [LoginResponseDto](doc//LoginResponseDto.md)
- [LogoutResponseDto](doc//LogoutResponseDto.md)
- [OAuthCallbackDto](doc//OAuthCallbackDto.md)
- [OAuthConfigDto](doc//OAuthConfigDto.md)
- [OAuthConfigResponseDto](doc//OAuthConfigResponseDto.md)
- [RemoveAssetsDto](doc//RemoveAssetsDto.md)
- [SearchAssetDto](doc//SearchAssetDto.md)
- [ServerInfoResponseDto](doc//ServerInfoResponseDto.md)
Expand Down
1 change: 1 addition & 0 deletions mobile/openapi/doc/LogoutResponseDto.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:openapi/api.dart';
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**successful** | **bool** | | [readonly]
**redirectUri** | **String** | | [readonly]

[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

Expand Down
97 changes: 97 additions & 0 deletions mobile/openapi/doc/OAuthApi.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# openapi.api.OAuthApi

## Load the API package
```dart
import 'package:openapi/api.dart';
```

All URIs are relative to */api*

Method | HTTP request | Description
------------- | ------------- | -------------
[**callback**](OAuthApi.md#callback) | **POST** /oauth/callback |
[**generateConfig**](OAuthApi.md#generateconfig) | **POST** /oauth/config |


# **callback**
> LoginResponseDto callback(oAuthCallbackDto)


### Example
```dart
import 'package:openapi/api.dart';
final api_instance = OAuthApi();
final oAuthCallbackDto = OAuthCallbackDto(); // OAuthCallbackDto |
try {
final result = api_instance.callback(oAuthCallbackDto);
print(result);
} catch (e) {
print('Exception when calling OAuthApi->callback: $e\n');
}
```

### Parameters

Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**oAuthCallbackDto** | [**OAuthCallbackDto**](OAuthCallbackDto.md)| |

### Return type

[**LoginResponseDto**](LoginResponseDto.md)

### Authorization

No authorization required

### HTTP request headers

- **Content-Type**: application/json
- **Accept**: application/json

[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)

# **generateConfig**
> OAuthConfigResponseDto generateConfig(oAuthConfigDto)


### Example
```dart
import 'package:openapi/api.dart';
final api_instance = OAuthApi();
final oAuthConfigDto = OAuthConfigDto(); // OAuthConfigDto |
try {
final result = api_instance.generateConfig(oAuthConfigDto);
print(result);
} catch (e) {
print('Exception when calling OAuthApi->generateConfig: $e\n');
}
```

### Parameters

Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**oAuthConfigDto** | [**OAuthConfigDto**](OAuthConfigDto.md)| |

### Return type

[**OAuthConfigResponseDto**](OAuthConfigResponseDto.md)

### Authorization

No authorization required

### HTTP request headers

- **Content-Type**: application/json
- **Accept**: application/json

[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)

15 changes: 15 additions & 0 deletions mobile/openapi/doc/OAuthCallbackDto.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# openapi.model.OAuthCallbackDto

## Load the model package
```dart
import 'package:openapi/api.dart';
```

## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**url** | **String** | |

[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)


15 changes: 15 additions & 0 deletions mobile/openapi/doc/OAuthConfigDto.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# openapi.model.OAuthConfigDto

## Load the model package
```dart
import 'package:openapi/api.dart';
```

## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**redirectUri** | **String** | |

[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)


17 changes: 17 additions & 0 deletions mobile/openapi/doc/OAuthConfigResponseDto.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# openapi.model.OAuthConfigResponseDto

## Load the model package
```dart
import 'package:openapi/api.dart';
```

## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**enabled** | **bool** | | [readonly]
**url** | **String** | | [optional] [readonly]
**buttonText** | **String** | | [optional] [readonly]

[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)


4 changes: 4 additions & 0 deletions mobile/openapi/lib/api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ part 'api/asset_api.dart';
part 'api/authentication_api.dart';
part 'api/device_info_api.dart';
part 'api/job_api.dart';
part 'api/o_auth_api.dart';
part 'api/server_info_api.dart';
part 'api/user_api.dart';

Expand Down Expand Up @@ -74,6 +75,9 @@ part 'model/job_status_response_dto.dart';
part 'model/login_credential_dto.dart';
part 'model/login_response_dto.dart';
part 'model/logout_response_dto.dart';
part 'model/o_auth_callback_dto.dart';
part 'model/o_auth_config_dto.dart';
part 'model/o_auth_config_response_dto.dart';
part 'model/remove_assets_dto.dart';
part 'model/search_asset_dto.dart';
part 'model/server_info_response_dto.dart';
Expand Down

1 comment on commit d3c35ec

@vercel
Copy link

@vercel vercel bot commented on d3c35ec Nov 15, 2022

Choose a reason for hiding this comment

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

Please sign in to comment.