(Solved) Can't get OAuth working with Seafile 13 CE + Authelia

Been bashing my head against a wall for days with this one. I previously had a Seafile 11 CE setup and working with my Authelia instance. I upgraded to 13 since I never got Seadoc working but now OAuth won’t work. I’ve done several clean reinstalls but I just can’t get it. In a futile attempt, I also tested Seafile 12 but no dice.

As it is, I can log in normally, upload/download files, Seadoc still doesn’t work but that’s for another day.

I have used the official docker configuration for install, but have disabled the built in Caddy container as I have an external Caddy instance in control of all incoming web traffic which handles all SSL with a wildcard certificate for my domain.

The problem

When I go to https://cloud.domain.com I can click on the “Single Sign-On” button and get redirected to https://auth.domain.com but authorising the request will sit there for a few minutes until it times out. I then get redirected back to Seafile, with the error page “Error, please contact administrator.”

(My) Caddyfile block

### SEAFILE - SSO
cloud.domain.com {
  tls /data/caddy/certificates/wildcards/domain.com/fullchain.pem /data/caddy/certificates/wildcards/domain.com/privkey.pem
  handle_path /sdoc-server/* {
    reverse_proxy http://192.168.2.4:8888 {
      header_up Host {host}
      header_up X-Real-IP {remote_host}
      }

    # 100 MB upload limit
    request_body {
      max_size 100MB
      }
    }
  handle_path /socket.io* {
    reverse_proxy http://192.168.2.4:8888 {
      header_up Host {host}
      header_up X-Real-IP {remote_host}
      header_up X-NginX-Proxy true
      header_up Upgrade {http.request.header.Upgrade}
      header_up Connection {http.request.header.Connection}

      # WebSocket support (HTTP/1.1)
      transport http {
        versions 1.1
        }
      }
    }
  reverse_proxy http://192.168.2.4:80 {
    header_up Host {host}
    header_up X-Real-IP {remote_host}
    header_up Connection ""

    transport http {
      read_timeout 310s
      }
    }

  request_body {
      max_size 0
  }
}

Built-in Caddy disabled in .env

#################################
# Docker compose configurations #
#################################
#COMPOSE_FILE='seafile-server.yml,caddy.yml,seadoc.yml'
COMPOSE_FILE='seafile-server.yml,seadoc.yml'
COMPOSE_PATH_SEPARATOR=','

Seafile docker has ports uncommented, and Caddy labels blanked

services:
...
  seafile:
    image: ${SEAFILE_IMAGE:-seafileltd/seafile-mc:13.0-latest}
    container_name: seafile
    restart: unless-stopped
    ports:  # uncommented
      - "80:80" # uncommented
...
#    labels:
#      caddy: ${SEAFILE_SERVER_PROTOCOL:-http}://${SEAFILE_SERVER_HOSTNAME:?Variable is not set or empty}
#      caddy.reverse_proxy: "{{upstreams 80}}"

I’ve done the same to the seadoc.yml, but I’ll leave it out to exclude more walls of text.

/opt/seafile-data/seafile/conf/seahub_settings.py

ENABLE_OAUTH = True

OAUTH_CREATE_UNKNOWN_USER = True
OAUTH_ACTIVATE_USER_AFTER_CREATION = True

Usually OAuth works through SSL layer. If your server is not parametrized to allow HTTPS, some method w>

#OAUTH_ENABLE_INSECURE_TRANSPORT = True
OAUTH_ENABLE_INSECURE_TRANSPORT = False

OAUTH_CLIENT_ID = “*random-client-string*”
OAUTH_CLIENT_SECRET = “secret-plaintext”

Callback url when user authentication succeeded. Note, the redirect url you input when you register you>

OAUTH_REDIRECT_URL = ‘https://cloud.domain.com/oauth/callback/’

#OAUTH_PROVIDER_DOMAIN = ‘auth.domain.com’
OAUTH_PROVIDER = ‘auth.domain.com’

OAUTH_AUTHORIZATION_URL = ‘https://auth.domain.com/api/oidc/authorization/’
OAUTH_TOKEN_URL = ‘https://auth.domain.com/api/oidc/token/’
OAUTH_USER_INFO_URL = ‘https://auth.domain.com/api/oidc/userinfo/’
OAUTH_SCOPE = [
“openid”,
“profile”,
“email”,
]

OAUTH_ATTRIBUTE_MAP = {
“id”: {True, “email”),
"uid / id / username": (True, "uid"),
“preferred_username”: (True, “uid”),   # Seafile v11.0 +
"name": (False, "name"),
"email": (False, "contact_email"),

}

Authelia Configuration

### SEAFILE
      - client_id: '*random-client-string*'
        client_name: 'Seafile'
        client_secret: '*secret-hashed*'
        public: false
        authorization_policy: 'two_factor'
        require_pkce: false
        pkce_challenge_method: ''
        redirect_uris:
          - 'https://cloud.domain.com/oauth/callback/'
        scopes:
          - 'openid'
          - 'profile'
          - 'email'
        response_types:
          - 'code'
        grant_types:
          - 'authorization_code'
        access_token_signed_response_alg: 'none'
        userinfo_signed_response_alg: 'none'
        token_endpoint_auth_method: 'client_secret_basic'
#        consent_mode: implicit

Caddy and Seafile are on separate hosts, but Caddy and Authelia are running together on a single docker stack with a shared network.

I know Seafile can reach Authelia as I can get a response from within the container’s shell:

# curl https://auth.domain.com
<!doctype html>
<html lang="en">
    <head>
        <base href="https://auth.domain.com/" />
        <meta property="csp-nonce" content="N94ORwOOjjlxctT4mdzD5Cv8A0UsF8kJ" />
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <meta name="theme-color" content="#000000" />
        <link rel="manifest" href="./manifest.json" />
        <link rel="icon" href="./favicon.ico" />
        <title></title>
      <script type="module" crossorigin src="./static/js/index.J4YSomth.js"></script>
      <link rel="stylesheet" crossorigin href="./static/css/index.ChexMFeY.css">
    </head>

    <body
        data-basepath=""
        data-duoselfenrollment="false"
        data-logooverride="false"
        data-privacypolicyurl=""
        data-privacypolicyaccept="false"
        data-passkeylogin="false"
        data-rememberme="true"
        data-resetpassword="true"
        data-resetpasswordcustomurl=""
        data-theme="auto"
    >
        <noscript>You need to enable JavaScript to run this app.</noscript>
        <div id="root"></div>
    </body>
</html>

And likewise, connecting to Seafile from the Authelia shell:

/app # wget cloud.domain.com
--2025-12-25 07:06:27--  http://cloud.domain.com/
Resolving cloud.domain.com (cloud.domain.com)... 10.15.1.218
Connecting to cloud.domain.com (cloud.domain.com)|10.15.1.218|:80... connected.
HTTP request sent, awaiting response... 308 Permanent Redirect
Location: https://cloud.domain.com/ [following]
--2025-12-25 07:06:27--  https://cloud.domain.com/
Connecting to cloud.domain.com (cloud.domain.com)|10.15.1.218|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: /accounts/login/?next=/ [following]
--2025-12-25 07:06:27--  https://cloud.domain.com/accounts/login/?next=/
Reusing existing connection to cloud.domain.com:443.
HTTP request sent, awaiting response... 200 OK
Length: 14825 (14K) [text/html]
Saving to: 'index.html'

index.html                                                    100%[==============================================================================================================================================>]  14.48K  --.-KB/s    in 0.001s  

2025-12-25 07:06:27 (21.3 MB/s) - 'index.html' saved [14825/14825]

Post continued…

Logs

I’ve collected a few logs but can’t make sense of them.

I see this one at startup, but I’ve double-checked and tinkered with the parameters so much I’m not sure anymore:

==> /opt/seafile-data/seafile/logs/seahub.log <==
[2025-12-23 06:37:37] [ERROR] seahub.oauth.views:168 oauth_callback (invalid_request) The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Client Credentials missing or malformed.

This is from the Authelia container logs. 10.15.1.1 is my PC, 172.20.0.2 is Caddy, and 172.20.0.3 is Authelia.

time="2025-12-23T06:47:19+11:00" level=debug msg="Authorization Request with id 'd6489da8-5f9f-4521-86a8-0ae7f009e918' on client with id '*random-client-string*' is being processed" method=GET path=/api/oidc/authorization remote_ip=10.15.1.1

time="2025-12-23T06:47:19+11:00" level=debug msg="Checking the authentication backend for an updated profile for user" method=GET path=/api/oidc/authorization remote_ip=10.15.1.1 username=aaron

time="2025-12-23T06:47:19+11:00" level=debug msg="Authorization Request with id 'd6489da8-5f9f-4521-86a8-0ae7f009e918' on client with id '*random-client-string*' using consent mode 'explicit' proceeding to generate a new consent session" method=GET path=/api/oidc/authorization remote_ip=10.15.1.1

time="2025-12-23T06:47:19+11:00" level=debug msg="Authorization Request with id 'd6489da8-5f9f-4521-86a8-0ae7f009e918' on client with id '*random-client-string*' is not being redirected for reauthentication" authenticated_at="2025-09-14 04:57:26 +0000 UTC" method=GET path=/api/oidc/authorization prompt= remote_ip=10.15.1.1 requested_at="2025-12-24 19:47:19.389211291 +0000 UTC"

time="2025-12-23T06:47:19+11:00" level=debug msg="Authorization Request with id 'd6489da8-5f9f-4521-86a8-0ae7f009e918' on client with id '*random-client-string* using consent mode 'explicit' authentication level 'two_factor' is sufficient for client level 'two_factor'" method=GET path=/api/oidc/authorization remote_ip=10.15.1.1

time="2025-12-23T06:47:19+11:00" level=debug msg="Authorization Request with id 'd6489da8-5f9f-4521-86a8-0ae7f009e918' on client with id '*random-client-string*' using consent mode 'explicit' is being redirected to 'https://auth.asareon.com/consent/openid/decision?flow=openid_connect&flow_id=e6c27477-c07b-46e4-b7a9-c705c5a7eca8'" method=GET path=/api/oidc/authorization remote_ip=10.15.1.1

time="2025-12-23T06:47:29+11:00" level=error msg="Request timeout occurred while handling request from client." error="read tcp 172.20.0.3:9091->172.20.0.2:51396: i/o timeout" method=GET path=/ remote_ip=172.20.0.2 stack="github.com/authelia/authelia/v4/internal/server/handlers.go:91 New.handleError.func2\ngithub.com/valyala/fasthttp@v1.68.0/server.go:2967             (*Server).writeErrorResponse\ngithub.com/valyala/fasthttp@v1.68.0/server.go:2394             (*Server).serveConn\ngithub.com/valyala/fasthttp@v1.68.0/workerpool.go:225          (*workerPool).workerFunc\ngithub.com/valyala/fasthttp@v1.68.0/workerpool.go:197          (*workerPool).getCh.func1\nruntime/asm_amd64.s:1693                                       goexit" status_code=408

time="2025-12-23T06:47:29+11:00" level=error msg="Request timeout occurred while handling request from client." error="read tcp 172.20.0.3:9091->172.20.0.2:51440: i/o timeout" method=GET path=/ remote_ip=172.20.0.2 stack="github.com/authelia/authelia/v4/internal/server/handlers.go:91 New.handleError.func2\ngithub.com/valyala/fasthttp@v1.68.0/server.go:2967             (*Server).writeErrorResponse\ngithub.com/valyala/fasthttp@v1.68.0/server.go:2394             (*Server).serveConn\ngithub.com/valyala/fasthttp@v1.68.0/workerpool.go:225          (*workerPool).workerFunc\ngithub.com/valyala/fasthttp@v1.68.0/workerpool.go:197          (*workerPool).getCh.func1\nruntime/asm_amd64.s:1693                                       goexit" status_code=408

time="2025-12-23T06:47:29+11:00" level=error msg="Request timeout occurred while handling request from client." error="read tcp 172.20.0.3:9091->172.20.0.2:51432: i/o timeout" method=GET path=/ remote_ip=172.20.0.2 stack="github.com/authelia/authelia/v4/internal/server/handlers.go:91 New.handleError.func2\ngithub.com/valyala/fasthttp@v1.68.0/server.go:2967             (*Server).writeErrorResponse\ngithub.com/valyala/fasthttp@v1.68.0/server.go:2394             (*Server).serveConn\ngithub.com/valyala/fasthttp@v1.68.0/workerpool.go:225          (*workerPool).workerFunc\ngithub.com/valyala/fasthttp@v1.68.0/workerpool.go:197          (*workerPool).getCh.func1\nruntime/asm_amd64.s:1693                                       goexit" status_code=408

Perhaps a more trained eye can help me see what I’m doing wrong, or point me in the right direction. I’m exhausted :sweat_smile:

Thanks!

I have seafile working with authelia. My setup sounds similar to yours, except I have nginx as the reverse proxy, and authelia is in a separate VM from seafile.

That log entry makes me think the problem might be in the OAUTH_SCOPE or OAUTH_ATTRIBUTE_MAP. Here is mine for comparison:

OAUTH_SCOPE = [ "openid", "profile", "email", ]
OAUTH_ATTRIBUTE_MAP = {
    "email": (True, "email"),
    "name": (False, "name"),
    "id": (False, "not used"),
    }

And I think the reason you can only use email, name, and id is that those are all authelia is configured to include in the authorization. Or at least I think that’s what this section does:

        scopes:
          - 'openid'
          - 'profile'
          - 'email'

Anyway, in case it helps here is the entire OAUTH settings section of my seahub_settings.py:

# ### OAuth authentication.  See: https://manual.seafile.com/deploy/oauth/
ENABLE_OAUTH = True

# # If create new user when he/she logs in Seafile for the first time, defalut `True`.
OAUTH_CREATE_UNKNOWN_USER = True

# # If active new user when he/she logs in Seafile for the first time, defalut `True`.
OAUTH_ACTIVATE_USER_AFTER_CREATION = True

# # Usually OAuth works through SSL layer. If your server is not parametrized to allow HTTPS, some method will raise an "oauthlib.oauth2.rfc6749.errors.InsecureTransportError". Set this to `True` to avoid this error.
OAUTH_ENABLE_INSECURE_TRANSPORT = False

# # Client id/secret generated by authorization server when you register your client application.
OAUTH_CLIENT_ID = "random_stuff_matching_authelia_config"
OAUTH_CLIENT_SECRET = "random_stuff_matching_authelia_config"

# # Callback url when user authentication succeeded. Note, the redirect url you input when you register your client application MUST be exactly the same as this value.
OAUTH_REDIRECT_URL = 'https://seafile.domain.com/oauth/callback/'

OAUTH_PROVIDER_DOMAIN = 'authelia.domain.com'
OAUTH_PROVIDER = 'authelia.domain.com'

OAUTH_AUTHORIZATION_URL = 'https://authelia.domain.com/api/oidc/authorization'
OAUTH_TOKEN_URL = 'https://authelia.domain.com/api/oidc/token'
OAUTH_USER_INFO_URL = 'https://authelia.domain.com/api/oidc/userinfo'
OAUTH_SCOPE = [ "openid", "profile", "email", ]
OAUTH_ATTRIBUTE_MAP = {
    "email": (True, "email"),
    "name": (False, "name"),
    "id": (False, "not used"),
    }

And here is the seafile part of my authelia conifg:

## Seafile
      - client_id: 'a random string'
        client_name: 'seafile'
        client_secret: 'another random string'
        public: false
        authorization_policy: 'two_factor'
        consent_mode: pre-configured
        pre_configured_consent_duration: 2w
        redirect_uris:
          - 'https://seafile.domain.com/oauth/callback/'
        scopes:
          - 'openid'
          - 'profile'
          - 'email'
        userinfo_signed_response_alg: 'none'
        token_endpoint_auth_method: 'client_secret_basic'

Thanks for chiming in, but no dice unfortunately. Like you said It looks a lot like mine, the only real difference is your OAUTH_ATTRIBUTE_MAP.

I’ve tried so many variations of OAUTH_ATTRIBUTE_MAP that I haven’t kept track. I recall having to fiddle a little bit before I got my Seafile v10 working, but the same configuration doesn’t work anymore, at least for me. And I naively haven’t kept a copy of it at this point.

I originally configured the current install based on the instructions on the Authelia docs ( https:// www. authelia. com/integration/openid-connect/clients/seafile/ ) but they’re for Seafile v10, so I’ve also pulled from the official Seafile v13 docs for OAuth ( https:// manual. seafile. com/13.0/config/oauth/#more-explanations-about-the-settings ) and used this snippet:

If you use a newly deployed 11.0+ Seafile instance, you don’t need the "id": (True, "email") item. Your configuration should be like:

    OAUTH_ATTRIBUTE_MAP = {
        "uid": (True, "uid") ,
        "name": (False, "name"),
        "email": (False, "contact_email"),  
    }

I just tried this setup again, for anyone interested here is the logs from Authelia:

time="2025-12-25T09:44:56+11:00" level=debug msg="Authorization Request with id 'beeaedb0-4e97-4d29-8d01-c2d9596eaa3b' on client with id '*random-client-string*' is being processed" method=GET path=/api/oidc/authorization remote_ip=10.15.1.1

time="2025-12-25T09:44:56+11:00" level=debug msg="Authorization Request with id 'beeaedb0-4e97-4d29-8d01-c2d9596eaa3b' on client with id '*random-client-string*' using consent mode 'explicit' proceeding to generate a new consent session" method=GET path=/api/oidc/authorization remote_ip=10.15.1.1

time="2025-12-25T09:44:56+11:00" level=debug msg="Authorization Request with id 'beeaedb0-4e97-4d29-8d01-c2d9596eaa3b' on client with id '*random-client-string*' is not being redirected for reauthentication" authenticated_at="2025-09-14 04:57:26 +0000 UTC" method=GET path=/api/oidc/authorization prompt= remote_ip=10.15.1.1 requested_at="2025-12-24 22:44:56.272040378 +0000 UTC"

time="2025-12-25T09:44:56+11:00" level=debug msg="Authorization Request with id 'beeaedb0-4e97-4d29-8d01-c2d9596eaa3b' on client with id '*random-client-string*' using consent mode 'explicit' authentication level 'two_factor' is sufficient for client level 'two_factor'" method=GET path=/api/oidc/authorization remote_ip=10.15.1.1

time="2025-12-25T09:44:56+11:00" level=debug msg="Authorization Request with id 'beeaedb0-4e97-4d29-8d01-c2d9596eaa3b' on client with id '*random-client-string*' using consent mode 'explicit' is being redirected to 'https://auth.domain.com/consent/openid/decision?flow=openid_connect&flow_id=2581ac86-464d-494a-95f7-d37764f5e823'" method=GET path=/api/oidc/authorization remote_ip=10.15.1.1

time="2025-12-25T09:44:58+11:00" level=debug msg="Authorization Request with id 'f74d1e43-14f4-42a3-9079-1925ad24ed9b' on client with id '*random-client-string*' is being processed" method=GET path=/api/oidc/authorization remote_ip=10.15.1.1

time="2025-12-25T09:44:58+11:00" level=debug msg="Authorization Request with id 'f74d1e43-14f4-42a3-9079-1925ad24ed9b' on client with id '*random-client-string*' was successfully processed, proceeding to build Authorization Response" method=GET path=/api/oidc/authorization remote_ip=10.15.1.1

time="2025-12-25T09:44:58+11:00" level=error msg="Access Request failed with error: The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Client Credentials missing or malformed. The Client ID was missing from the request but it is required when there is no client assertion." method=POST path=/api/oidc/token remote_ip=10.20.1.217 stack="github.com/authelia/authelia/v4/internal/handlers/handler_oauth2_token.go:25                OAuth2TokenPOST\ngithub.com/authelia/authelia/v4/internal/middlewares/http_to_authelia_handler_adaptor.go:58 RegisterOpenIDConnectRoutes.NewHTTPToAutheliaHandlerAdaptor.func17\ngithub.com/authelia/authelia/v4/internal/middlewares/bridge.go:66                           RegisterOpenIDConnectRoutes.(*BridgeBuilder).Build.func2.1\ngithub.com/authelia/authelia/v4/internal/middlewares/headers.go:105                         SecurityHeadersNoStore.func1\ngithub.com/valyala/fasthttp@v1.68.0/server.go:773                                           (*RequestCtx).UserValue\ngithub.com/authelia/authelia/v4/internal/middlewares/headers.go:30                          SecurityHeadersBase.func1\ngithub.com/authelia/authelia/v4/internal/middlewares/cors.go:216                            RegisterOpenIDConnectRoutes.(*CORSPolicy).Middleware.func18\ngithub.com/fasthttp/router@v1.5.4/router.go:441                                             (*Router).Handler\ngithub.com/authelia/authelia/v4/internal/middlewares/log_request.go:14                      handlerMain.LogRequest.func30\ngithub.com/authelia/authelia/v4/internal/middlewares/errors.go:38                           RecoverPanic.func1\ngithub.com/valyala/fasthttp@v1.68.0/server.go:2465                                          (*Server).serveConn\ngithub.com/valyala/fasthttp@v1.68.0/workerpool.go:225                                       (*workerPool).workerFunc\ngithub.com/valyala/fasthttp@v1.68.0/workerpool.go:197                                       (*workerPool).getCh.func1\nruntime/asm_amd64.s:1693                                                                    goexit"

time="2025-12-25T09:45:07+11:00" level=error msg="Request timeout occurred while handling request from client." error="read tcp 172.20.0.3:9091->172.20.0.2:58264: i/o timeout" method=GET path=/ remote_ip=172.20.0.2 stack="github.com/authelia/authelia/v4/internal/server/handlers.go:91 New.handleError.func2\ngithub.com/valyala/fasthttp@v1.68.0/server.go:2967             (*Server).writeErrorResponse\ngithub.com/valyala/fasthttp@v1.68.0/server.go:2394             (*Server).serveConn\ngithub.com/valyala/fasthttp@v1.68.0/workerpool.go:225          (*workerPool).workerFunc\ngithub.com/valyala/fasthttp@v1.68.0/workerpool.go:197          (*workerPool).getCh.func1\nruntime/asm_amd64.s:1693                                       goexit" status_code=408

time="2025-12-25T09:45:07+11:00" level=error msg="Request timeout occurred while handling request from client." error="read tcp 172.20.0.3:9091->172.20.0.2:58282: i/o timeout" method=GET path=/ remote_ip=172.20.0.2 stack="github.com/authelia/authelia/v4/internal/server/handlers.go:91 New.handleError.func2\ngithub.com/valyala/fasthttp@v1.68.0/server.go:2967             (*Server).writeErrorResponse\ngithub.com/valyala/fasthttp@v1.68.0/server.go:2394             (*Server).serveConn\ngithub.com/valyala/fasthttp@v1.68.0/workerpool.go:225          (*workerPool).workerFunc\ngithub.com/valyala/fasthttp@v1.68.0/workerpool.go:197          (*workerPool).getCh.func1\nruntime/asm_amd64.s:1693                                       goexit" status_code=408

time="2025-12-25T09:45:07+11:00" level=error msg="Request timeout occurred while handling request from client." error="read tcp 172.20.0.3:9091->172.20.0.2:58278: i/o timeout" method=GET path=/ remote_ip=172.20.0.2 stack="github.com/authelia/authelia/v4/internal/server/handlers.go:91 New.handleError.func2\ngithub.com/valyala/fasthttp@v1.68.0/server.go:2967             (*Server).writeErrorResponse\ngithub.com/valyala/fasthttp@v1.68.0/server.go:2394             (*Server).serveConn\ngithub.com/valyala/fasthttp@v1.68.0/workerpool.go:225          (*workerPool).workerFunc\ngithub.com/valyala/fasthttp@v1.68.0/workerpool.go:197          (*workerPool).getCh.func1\nruntime/asm_amd64.s:1693                                       goexit" status_code=408

And the same error in Seafile logs:

==> /opt/seafile-data/seafile/logs/seahub.log <==
[2025-12-25 09:50:27] [ERROR] seahub.oauth.views:168 oauth_callback (invalid_request) The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Client Credentials missing or malformed.

I’m not 100% certain what credentials its talking about. I assume its OAUTH_CLIENT_ID and OAUTH_CLIENT_SECRET?

I’ve replaced them twice and double, tripled checked… I was certain I’ve got it right but this error is making me seriously double guess my eyes.

To remove potential formatting or interpreter issues, the OAUTH_CLIENT_ID is letters and numbers only.

OAUTH_CLIENT_SECRET is letters, numbers, and some special characters, dot, dash, and tilde ( . - ~ ).

I generated the SECRET using the docker one-liner provided by Authelia ( https:// www. authelia. com/integration/openid-connect/frequently-asked-questions/#client-secret ):

docker run --rm authelia/authelia:latest authelia crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986

seahub_settings.py uses quotes ( “ “ ) around the ID, and Authelia configuration uses single quotes ( ‘ ‘ ), and likewise for the SECRET.

I’ve used the same docker lines from Authelia for multiple other services like immich and paperless, and they’re fine.

Sorry about the broken links - this forum hyperlinks automatically but I don’t have permission to post them.

I’m not an expert, but I think you are right there, I think that is the OAUTH_CLIENT_ID, which has to match exactly the client_id: from the authelia config. I never noticed until you pointed it out that it is quoted with " in one file, and ’ in the other, but mine is the same way and it is working, so it must be fine.

It might be that there’s some upper limit to the string’s length or something similar breaking the string on one end. I had some trouble when I was setting it up that I thought was related to the client_id but I don’t remember the details. I do remember that what I finally did was change it to “client_id: ‘seafile’” (and change the seafile side to match). Then once it was working, I added some random text so it so it now looks like “client_id: ‘seafile_ezlmpupmyu’”. I think there’s supposed to be some randomness to keep it from being easily guessed, but I don’t think much is needed (the real protection is in client_secret, this is some belt-and-suspenders safety practice).

So I think try just setting to “seafile” or something easy like that, and then add back complexity when it is working.

Thanks for your input so far. I’ve tried to break this down more for it to be easier to read and understand.

For extra information, I do have several subnets in use.

  • 10.15.1.0/24 is my “public/wifi”. Caddy is on 10.15.1.218, responding to all requests including for “cloud .domain .com”
  • 10.20.1.0/24 is for “servers”. For servers I dont want having on the same subnet as my wireless devices. Caddy is also on this subnet as 10.20.1.218, responding to all requests. Seafile lives on this subnet, with IP 10.20.1.217.
  • 192.168.2.0/24 is the “backend” on my proxmox host. A private network given only to a couple of services for Saddy to send traffic directly to, rather than going out through the switch. Caddy has 192.168.2.2, Seafile is on 192.168.2.4.

All my OAuth services use 192.168.2.0/24.

So to recap, the usual path of traffic is

  • HTTP/S traffic for “cloud .domain .com” go to Caddy on 10.15.1.218
  • Caddy sends this traffic through to Seafile on 192.168.2.4
  • Clicking “Single Sign-On” in Seafile sends the authorisation request to “auth .domain .com” which lives next to Caddy on 10.15.1.218, with a shared docker network (172.20.0.2, 172.20.0.3)
  • Once authorised, Authelia “should” send me back to “cloud .domain .com”

For testing, I have removed the backend subnet from the equation:

  • Changed “cloud .domain .com” to 10.20.1.218 (instead of 10.15.1.218)
  • Change the Caddy config to point straight to Seafile on 10.20.1.217
  • Allowed all traffic (tcp and udp) between Caddy and Seafile (10.20.1.218, 10.20.1.217)
  • Confirmed that my pc on 10.15.1.1, has full access to 10.20.1.0/24 on the router’s firewall.
  • I’ve replaced the credentials entirely, reducing the length from 72 to 20 with the docker command provided by Authelia:
root@caddy:~# docker run --rm authelia/authelia:latest authelia crypto hash generate pbkdf2 --variant sha512 --random --random.length 20 --random.charset rfc3986
Random Password: StujpXn7h~YUyXXAQ9tS
Digest: $pbkdf2-sha512$310000$10QJVuo0RAC9K5bdi1qZ2w$32UiCqlotLyVBvUyesvTA8jYoiN4sGb1Sj9rTIe0cIFmUDAMDvh9BmoK0/fousshJTHul..Dn7ejPufCw4nokw

Of course, this password will be changed once I get this damn thing fixed :cry:

Here they are, in the Seafile settings: /opt/seafile-data/seafile/conf/seahub_settings.py

...
OAUTH_CLIENT_ID = "seafile123"
OAUTH_CLIENT_SECRET = "StujpXn7h~YUyXXAQ9tS"
...

Here’s the seafile configuration in Authelia’s configuration.yml

### SEAFILE
      - client_id: 'seafile123'
        client_name: 'Seafile'
        client_secret: '$pbkdf2-sha512$310000$10QJVuo0RAC9K5bdi1qZ2w$32UiCqlotLyVBvUyesvTA8jYoiN4sGb1Sj9rTIe0cIFmUDAMDvh9BmoK0/fousshJTHul..Dn7ejPufCw4nokw'
        public: false
        authorization_policy: 'seafile_acl'
        require_pkce: false
        pkce_challenge_method: ''
        redirect_uris:
          - 'https://cloud.domain.com/oauth/callback/'
        scopes:
          - openid
          - profile
          - email
        response_types:
          - 'code'
        grant_types:
          - 'authorization_code'
        access_token_signed_response_alg: 'none'
        userinfo_signed_response_alg: 'none'
        token_endpoint_auth_method: 'client_secret_basic'
#        consent_mode: implicit

I realised the “authorization_policy:” was set to “two_factor” but my other OAuth services had their own acl. So, I created seafile_acl and assigned it.

Auth policy for Seafile (with my other acl’s for working services), configuration.yml

identity_providers:
  oidc:
    ## The other portions of the mandatory OpenID Connect 1.0 configuration go here.
    ## See: https://www.authelia.com/c/oidc
    jwks:
      - key: {{ secret "/config/secrets/oidc/jwks/private.pem" | mindent 10 "|" | msquote }}
    authorization_policies:
      immich_acl:
        rules:
          - policy: 'deny'
            subject: 'group:friends'
      paperless_acl:
        rules:
          - policy: 'deny'
            subject: 'group:friends'
      seafile_acl:
        rules:
          - policy: 'deny'
            subject: 'group:friends'
    clients:

Relevant part of Authelia users_database.yml, showing I’m not in the “friends” group:

users:
  myself:
  ...
    groups:
      - family

But, I’m still getting the same error.

==> /opt/seafile-data/seafile/logs/seahub.log <==
[2025-12-26 11:19:30] [ERROR] seahub.oauth.views:168 oauth_callback (invalid_request) The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Client Credentials missing or malformed.
root@caddy:~# docker logs authelia -f

time="2025-12-26T11:19:25+11:00" level=debug msg="Authorization Request with id '53d1125a-370b-4404-bd1e-8a51724c439d' on client with id 'seafile123' is being processed" method=GET path=/api/oidc/authorization remote_ip=10.15.1.1

time="2025-12-26T11:19:25+11:00" level=debug msg="Checking the authentication backend for an updated profile for user" method=GET path=/api/oidc/authorization remote_ip=10.15.1.1 username=myuser

time="2025-12-26T11:19:25+11:00" level=debug msg="Authorization Request with id '53d1125a-370b-4404-bd1e-8a51724c439d' on client with id 'seafile123' using consent mode 'explicit' proceeding to generate a new consent session" method=GET path=/api/oidc/authorization remote_ip=10.15.1.1

time="2025-12-26T11:19:25+11:00" level=debug msg="Authorization Request with id '53d1125a-370b-4404-bd1e-8a51724c439d' on client with id 'seafile123' is not being redirected for reauthentication" authenticated_at="2025-09-14 04:57:26 +0000 UTC" method=GET path=/api/oidc/authorization prompt= remote_ip=10.15.1.1 requested_at="2025-12-26 00:19:25.285324323 +0000 UTC"

time="2025-12-26T11:19:25+11:00" level=debug msg="Authorization Request with id '53d1125a-370b-4404-bd1e-8a51724c439d' on client with id 'seafile123' using consent mode 'explicit' authentication level 'two_factor' is sufficient for client level 'two_factor'" method=GET path=/api/oidc/authorization remote_ip=10.15.1.1

time="2025-12-26T11:19:25+11:00" level=debug msg="Authorization Request with id '53d1125a-370b-4404-bd1e-8a51724c439d' on client with id 'seafile123' using consent mode 'explicit' is being redirected to 'https://auth.domain.com/consent/openid/decision?flow=openid_connect&flow_id=c7f96987-8797-4a03-954a-fd1956a80479'" method=GET path=/api/oidc/authorization remote_ip=10.15.1.1

time="2025-12-26T11:19:28+11:00" level=debug msg="Authorization Request with id '293abfd5-a785-4c03-8adf-34cc34d92c0e' on client with id 'seafile123' is being processed" method=GET path=/api/oidc/authorization remote_ip=10.15.1.1

time="2025-12-26T11:19:28+11:00" level=debug msg="Authorization Request with id '293abfd5-a785-4c03-8adf-34cc34d92c0e' on client with id 'seafile123' was successfully processed, proceeding to build Authorization Response" method=GET path=/api/oidc/authorization remote_ip=10.15.1.1

time="2025-12-26T11:19:30+11:00" level=error msg="Access Request failed with error: The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Client Credentials missing or malformed. The Client ID was missing from the request but it is required when there is no client assertion." method=POST path=/api/oidc/token remote_ip=10.20.1.217 stack="github.com/authelia/authelia/v4/internal/handlers/handler_oauth2_token.go:25                OAuth2TokenPOST\ngithub.com/authelia/authelia/v4/internal/middlewares/http_to_authelia_handler_adaptor.go:58 RegisterOpenIDConnectRoutes.NewHTTPToAutheliaHandlerAdaptor.func17\ngithub.com/authelia/authelia/v4/internal/middlewares/bridge.go:66                           RegisterOpenIDConnectRoutes.(*BridgeBuilder).Build.func2.1\ngithub.com/authelia/authelia/v4/internal/middlewares/headers.go:105                         SecurityHeadersNoStore.func1\ngithub.com/valyala/fasthttp@v1.68.0/server.go:773                                           (*RequestCtx).UserValue\ngithub.com/authelia/authelia/v4/internal/middlewares/headers.go:30                          SecurityHeadersBase.func1\ngithub.com/authelia/authelia/v4/internal/middlewares/cors.go:216                            RegisterOpenIDConnectRoutes.(*CORSPolicy).Middleware.func18\ngithub.com/fasthttp/router@v1.5.4/router.go:441                                             (*Router).Handler\ngithub.com/authelia/authelia/v4/internal/middlewares/log_request.go:14                      handlerMain.LogRequest.func30\ngithub.com/authelia/authelia/v4/internal/middlewares/errors.go:38                           RecoverPanic.func1\ngithub.com/valyala/fasthttp@v1.68.0/server.go:2465                                          (*Server).serveConn\ngithub.com/valyala/fasthttp@v1.68.0/workerpool.go:225                                       (*workerPool).workerFunc\ngithub.com/valyala/fasthttp@v1.68.0/workerpool.go:197                                       (*workerPool).getCh.func1\nruntime/asm_amd64.s:1693

I guess I’ll go post this on the Authelia forums as well, but I don’t think its the problem - again, I have my immich and paperless services working on both 10.20.1.0/24 and 192.168.2.0/24.

I think you’ve covered everything I’ve thought of. You checked that both can access the URL of the other, etc. I think if it were me, I would next try a tcpdump to grab the communication between authelia and seafile, and then view it in wireshark. I don’t really know what you would be looking for there, but when I have no other ideas I find it sometimes helps me to just look for anything weird in wireshark (stuff going to the wrong IP, wrong names being looked up in DNS, etc).

In case it helps, I will paste in my configs here for you to compare to. My config files are loaded on these server through ansible, so where you see things like {{ something }} it is a variable that will be replaced with a value by ansible.

Much like you described, I have my servers on a separate vlan from everything else, but on mine they are all on the same servers vlan.

Authelia config (with bits not relevant to seafile omitted):

certificates_directory: '/data/authelia/config'
theme: 'auto'
default_2fa_method: 'totp'
server:
  address: 'tcp4://:{{ authelia_port }}/'

  endpoints:
    authz:
      auth-request:
        implementation: 'AuthRequest'

log:
  ## Level of verbosity for logs: info, debug, trace.
  level: 'debug'
  format: 'text'
  file_path: /data/authelia/logs/log.log
  keep_stdout: true

telemetry:
  metrics:
    enabled: false

totp:
  disable: false
  issuer: {{ primary_public_domain }}
  algorithm: 'SHA1'
  digits: 6
  period: 30
  secret_size: 32
  disable_reuse_security_policy: false

webauthn:
  disable: false
  timeout: '60 seconds'
  display_name: '{{ primary_public_domain }}'
  attestation_conveyance_preference: 'indirect'
  selection_criteria:
    attachment: ''
    discoverability: 'preferred'
    user_verification: 'preferred'

identity_validation:
  reset_password:
    jwt_lifespan: '5 minutes'
    jwt_algorithm: 'HS256'
    jwt_secret: '{{ auth_password_reset_jwt }}'

  elevated_session:
    code_lifespan: '5 minutes'
    elevation_lifespan: '10 minutes'
    characters: 8
    require_second_factor: true
    skip_second_factor: true

ntp:
  address: 'udp4://192.168.69.1:123'
  version: 4

authentication_backend:
... skipping a bunch of settings for LDAP ...

password_policy:
  standard:
    enabled: true
    min_length: 8
    max_length: 0
    require_uppercase: true
    require_lowercase: true
    require_number: true
    require_special: true
  zxcvbn:
    enabled: false
    min_score: 3

privacy_policy:
  enabled: false
  require_user_acceptance: false
  policy_url: ''

access_control:
  default_policy: 'deny'
  networks:
    - name: 'internal'
      networks:
        - '127.0.0.1/24'
        - '{{ main_network_subnet }}'
        - '{{ dmz_network_subnet }}'
        # include the VPNs too
        - '{{ primary_vpn_subnet }}'
        - '{{ backup_vpn_subnet }}'

    - name: 'dmz'
      networks:
        - '{{ dmz_network_subnet }}'
  rules:
    - domain: {{ hostvars['seafile'].subdomain }}.{{ primary_public_domain }}
      policy: 'bypass'

session:
  secret: '{{ session_secret }}'
  cookies:
    - domain: '{{ primary_public_domain }}'
      authelia_url: 'https://{{ public_address }}'
  name: 'auth_session'
  same_site: 'lax'  ##TODO:  Review if lax is really what we want
  inactivity: '{{ inactivity_timeout }}'
  expiration: '{{ normal_session_expiration }}'
  remember_me: '{{ remember_me_session_expiration }}'
{% if use_redis_sessions is defined and use_redis_sessions == true %}
  redis:
    host: '{{ redis_host }}'
    port: {{ redis_port }}
    timeout: '5s'
    max_retries: 4
{% if redis_user is defined %}
    username: '{{ redis_user }}'
{% endif %}
{% if redis_password is defined %}
    password: '{{ redis_password }}'
{% endif %}
{% endif %}

regulation:
  modes:
    - 'user'
    - 'ip'
  max_retries: 4
  # Find_time can't be more than ban_time
  find_time: '2 hours'
  ban_time: '2 hours'

storage:
  encryption_key: '{{ authelia_storage_key }}'
  mysql:
    address: 'tcp://127.0.0.1:3306'
    database: '{{ authelia_db_name }}'
    username: '{{ authelia_db_user }}'
    password: '{{ authelia_db_password }}'
    timeout: '5 seconds'

notifier:
... skip settings related to SMTP server ...

identity_providers:
  oidc:
    enable_client_debug_messages: false
    minimum_parameter_entropy: 8
    enforce_pkce: 'public_clients_only'
    enable_pkce_plain_challenge: false
    enable_jwt_access_token_stateless_introspection: false
    discovery_signed_response_alg: 'none'
    discovery_signed_response_key_id: ''
    require_pushed_authorization_requests: false
    authorization_policies:
      policy_name:
        default_policy: 'one_factor'
        rules:
          - policy: 'deny'
            subject: 'group:services'
    lifespans:
      access_token: '1h'
      authorize_code: '2m'
      id_token: '1h'
      refresh_token: '90m'
    clients:
      - client_id: '{{ authelia_seafile_id }}'
        client_name: 'seafile'
        client_secret: '{{ authelia_seafile_secret }}'
        public: false
        authorization_policy: 'two_factor'
        consent_mode: pre-configured
        pre_configured_consent_duration: 2w
        redirect_uris:
          - 'https://{{ hostvars['seafile'].subdomain }}.{{ primary_public_domain }}/oauth/callback/'
        scopes:
          - 'openid'
          - 'profile'
          - 'email'
        userinfo_signed_response_alg: 'none'
        token_endpoint_auth_method: 'client_secret_basic'

    jwks:
      - key_id: 'main'
        key: {{ authelia_jwks_key }}
        certificate_chain: {{ authelia_jwks_cert_chain }}

Seafile docker .env file. I should point out that I am not using docker, I am running in podman containers, using the docker config files with minimal changes. I tested briefly with seadoc, the metadata server, and the thumbnail server, but since these components don’t seem to be open source, I am currently not using any of them.

COMPOSE_FILE='seafile-server.yml
{%- if use_notification_server is defined and use_notification_server is true -%}
,notification-server.yml
{%- endif -%}
{%- if use_metadata_server is defined and use_metadata_server is true -%}
,md-server.yml
{%- endif -%}
{%- if use_thumbnail_server is defined and use_thumbnail_server is true -%}
,thumbnail-server.yml
{%- endif -%}
'

COMPOSE_PATH_SEPARATOR=','
NON_ROOT=true


SEAFILE_IMAGE=seafileltd/seafile-mc:{{ seafile_docker_version }}
SEAFILE_DB_IMAGE=mariadb:10.11
SEAFILE_REDIS_IMAGE=redis
MD_IMAGE=seafileltd/seafile-md-server:{{ seafile_metadata_server_version }}
THUMBNAIL_SERVER_IMAGE=seafileltd/thumbnail-server:{{ seafile_thumbnail_server_version }}


## Persistent Storage
BASIC_STORAGE_PATH={{ basic_volume_path }}
SEAFILE_VOLUME={{ seafile_volume_path }}
SEAFILE_MYSQL_VOLUME={{ seafile_db_volume_path }}
SEADOC_VOLUME=$BASIC_STORAGE_PATH/seadoc-data

TIME_ZONE={{ seafile_timezone }}

JWT_PRIVATE_KEY={{ jwt_private_key }}

SEAFILE_SERVER_HOSTNAME={{ public_address }}
SEAFILE_SERVER_PROTOCOL=https

## DATABASE
SEAFILE_MYSQL_DB_HOST=db
SEAFILE_MYSQL_DB_USER={{ seafile_db_user }}
SEAFILE_MYSQL_DB_PASSWORD={{ seafile_db_password }}
SEAFILE_MYSQL_DB_CCNET_DB_NAME={{ ccnet_db_name }}
SEAFILE_MYSQL_DB_SEAFILE_DB_NAME={{ seafile_db_name }}
SEAFILE_MYSQL_DB_SEAHUB_DB_NAME={{ seahub_db_name }}

## Cache
CACHE_PROVIDER=redis # or memcached

### Redis
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD={{ seafile_redis_pw }}

### Memcached
MEMCACHED_HOST=memcached
MEMCACHED_PORT=11211

######################################
#        Initial variables           #
# (Only valid in first-time startup) #
######################################

## Database root password, Used to create Seafile users
INIT_SEAFILE_MYSQL_ROOT_PASSWORD={{ mysql_root_password }}

## Seafile admin user
INIT_SEAFILE_ADMIN_EMAIL={{ seafile_default_admin_email }}
INIT_SEAFILE_ADMIN_PASSWORD={{ seafile_default_admin_pw }}

############################################
# Additional configurations for extensions #
############################################

## SeaDoc service
ENABLE_SEADOC=false

## Notification
{% if use_notification_server is defined and use_notification_server is true %}
ENABLE_NOTIFICATION_SERVER=true
NOTIFICATION_SERVER_URL=https://{{ public_address }}/notification
# INNER_NOTIFICATION_SERVER_URL
NOTIFICATION_SERVER_IMAGE=seafileltd/notification-server:{{ notification_docker_version }}
NOTIFICATION_SERVER_VOLUME={{ notification_volume_path }}
{% else %}
ENABLE_NOTIFICATION_SERVER=false
{% endif %}

## Seafile AI  --- ## haven't tested seafile AI yet ## --
ENABLE_SEAFILE_AI=false
SEAFILE_AI_LLM_URL=
SEAFILE_AI_LLM_KEY=

## Metadata server
MD_FILE_COUNT_LIMIT=100000

seafile-server.conf is entirely the default supplied by seafile docs, except for the container image version is set manually so that updates don’t happen until they have been tested in the test environment first, and the public address changed to a higher port number so the container can be run without needing root access.

seahub_settings.py. There are probably a lot of old and deprecated settings in here, since I have been running a seafile server for I thiink over 10 years.

SECRET_KEY = "{{ seahub_secret_key }}"
TIME_ZONE = '{{ seafile_timezone }}'


### CSRF stuff
# For security consideration, please set to match the host/domain of your site, e.g., ALLOWED_HOSTS = ['.example.com'].
# Please refer https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts for details.
ALLOWED_HOSTS = ['.{{ public_address }}','{{ ansible_host }}','127.0.0.1']

# https://docs.djangoproject.com/en/3.2/ref/settings/#csrf-trusted-origins
CSRF_TRUSTED_ORIGINS = ['https://{{ public_address }}']

# Whether to use a secure cookie for the CSRF cookie
# https://docs.djangoproject.com/en/3.2/ref/settings/#csrf-cookie-secure
CSRF_COOKIE_SECURE = True

# The value of the SameSite flag on the CSRF cookie
# https://docs.djangoproject.com/en/3.2/ref/settings/#csrf-cookie-samesite
CSRF_COOKIE_SAMESITE = 'Strict'



# Set this to your website/company's name. This is contained in email notifications and welcome message when user login for the first time.
SITE_NAME = 'Seafile'

# Browser tab's title
SITE_TITLE = 'Private Seafile'

# Get web api auth token on profile page.
ENABLE_GET_AUTH_TOKEN_BY_SESSION = True


# Enable two factor authentication for accounts. Defaults to `False`.
# Since version 6.0
ENABLE_TWO_FACTOR_AUTH = True

# If show contact email when search user.
ENABLE_SHOW_CONTACT_EMAIL_WHEN_SEARCH_USER = False

# Remember days for login. Default is 7
LOGIN_REMEMBER_DAYS = 30

ENABLE_SIGNUP = False
# deactivate user account when login attempts exceed limit
# Since version 5.1.2 or pro 5.1.3
FREEZE_USER_ON_LOGIN_FAILED = True
# Attempt limit before showing a captcha when login.
LOGIN_ATTEMPT_LIMIT = 3

# mininum length for user's password
USER_PASSWORD_MIN_LENGTH = 6

# LEVEL based on four types of input:
# num, upper letter, lower letter, other symbols
# '3' means password must have at least 3 types of the above.
USER_PASSWORD_STRENGTH_LEVEL = 3

# default False, only check USER_PASSWORD_MIN_LENGTH
# when True, check password strength level, STRONG(or above) is allowed
USER_STRONG_PASSWORD_REQUIRED = True

# Force user to change password when admin add/reset a user.
# Added in 5.1.1, deafults to True.
FORCE_PASSWORD_CHANGE = True

# Whether enable the feature "published library". Default is `False`
# Since 6.1.0 CE
ENABLE_WIKI = False


# In old version, if you use Single Sign On, the password is not
# saved in Seafile.  Users can't use WebDAV because Seafile can't
# check whether the password is correct.  Since version 6.3.8,
# you can enable this option to let user's to specific a password
# for WebDAV login.
#
# Users login via SSO can use this password to login in WebDAV.
# Enable the feature. pycryptodome should be installed first.
# sudo pip install pycryptodome==3.12.0
### It's picky, "false" causes seahub to fail to start, so try False
ENABLE_WEBDAV_SECRET = False
WEBDAV_SECRET_MIN_LENGTH = 8

# LEVEL for the password, based on four types of input:
# num, upper letter, lower letter, other symbols
# '3' means password must have at least 3 types of the above.
WEBDAV_SECRET_STRENGTH_LEVEL = 3


# Since version 7.0.9, you can force a full user to log in with a two
# factor authentication.  The prerequisite is that the administrator
# should 'enable two factor authentication' in the
# 'System Admin -> Settings' page.  Then you can add the following
# configuration information to the configuration file.

# ENABLE_FORCE_2FA_TO_ALL_USERS = True


# if enable create encrypted library
ENABLE_ENCRYPTED_LIBRARY = True

# version for encrypted library
# should only be `2` or `4`.
# version 3 is insecure (using AES128 encryption) so it's not recommended any more.
ENCRYPTED_LIBRARY_VERSION = 4

# Since version 12, you can choose password hash algorithm for new encrypted libraries.
# The password is used to encrypt the encryption key. So using a secure password hash algorithm to
# prevent brute-force password guessing is important.
# Before version 12, a fixed algorithm (PBKDF2-SHA256 with 1000 iterations) is used.
#
# Currently two hash algorithms are supported.
#  - PBKDF2: The only available parameter is the number of iterations. You need to increase the
#    the number of iterations over time, as GPUs are more and more used for such calculation.
#    The default number of iterations is 1000. As of 2023, the recommended iterations is 600,000.
#  - Argon2id: Secure hash algorithm that has high cost even for GPUs. There are 3 parameters that
#    can be set: time cost, memory cost, and parallelism degree. The parameters are seperated by commas,
#    e.g. "2,102400,8", which the default parameters used in Seafile. Learn more about this algorithm
#    on https://github.com/P-H-C/phc-winner-argon2 .
#
# Note that only sync client >= 9.0.9 and SeaDrive >= 3.0.12 supports syncing libraries created with these algorithms.
ENCRYPTED_LIBRARY_PWD_HASH_ALGO = "argon2id"
ENCRYPTED_LIBRARY_PWD_HASH_PARAMS = "{{ seafile_argon_params }}"

# mininum length for password of encrypted library
REPO_PASSWORD_MIN_LENGTH = 8

# force use password when generate a share/upload link (since version 8.0.9)
SHARE_LINK_FORCE_USE_PASSWORD = False

# Enable or disable library history setting
ENABLE_REPO_HISTORY_SETTING = True

# Enable or disable user share library to any group
# Since version 6.2.0
ENABLE_SHARE_TO_ALL_GROUPS = True

# Enable or disable user to clean trash (default is True)
# Since version 6.3.6
ENABLE_USER_CLEAN_TRASH = True

# Turn on this option to let users to add a label to a library snapshot. Default is `False`
#ENABLE_REPO_SNAPSHOT_LABEL = False
ENABLE_REPO_SNAPSHOT_LABEL = True

##### import of configs from old system
# From 6.1.0 CE version on, Seafile support viewing/editing **doc**, **ppt**, **xls** files via LibreOffice
# Add this setting to view/edit **doc**, **ppt**, **xls** files
OFFICE_SERVER_TYPE = 'CollaboraOffice'

# Enable LibreOffice Online
ENABLE_OFFICE_WEB_APP = True

# Url of LibreOffice Online's discovery page
# The discovery page tells Seafile how to interact with LibreOffice Online when view file online
# You should change `https://collabora-online.seafile.com/hosting/discovery` to your actual LibreOffice Online server address

OFFICE_WEB_APP_BASE_URL = '{{ seafile_collabora_address }}'


# Expiration of WOPI access token
# WOPI access token is a string used by Seafile to determine the file's
# identity and permissions when use LibreOffice Online view it online
# And for security reason, this token should expire after a set time period
WOPI_ACCESS_TOKEN_EXPIRATION = 240 * 60   # seconds


# List of file formats that you want to view through LibreOffice Online
# You can change this value according to your preferences
# And of course you should make sure your LibreOffice Online supports to preview
# the files with the specified extensions
OFFICE_WEB_APP_FILE_EXTENSION = ('odp', 'ods', 'odt', 'xls', 'xlsb', 'xlsm', 'xlsx','ppsx', 'ppt', 'pptm', 'pptx', 'doc', 'docm', 'docx')


# Enable edit files through LibreOffice Online
ENABLE_OFFICE_WEB_APP_EDIT = True


# types of files should be editable through LibreOffice Online
OFFICE_WEB_APP_EDIT_FILE_EXTENSION = ('odp', 'ods', 'odt', 'xls', 'xlsb', 'xlsm', 'xlsx','ppsx', 'ppt', 'pptm', 'pptx', 'doc', 'docm', 'docx')


# ### OAuth authentication.  See: https://manual.seafile.com/deploy/oauth/
ENABLE_OAUTH = True

# # If create new user when he/she logs in Seafile for the first time, defalut `True`.
OAUTH_CREATE_UNKNOWN_USER = True

# # If active new user when he/she logs in Seafile for the first time, defalut `True`.
OAUTH_ACTIVATE_USER_AFTER_CREATION = True

# # Usually OAuth works through SSL layer. If your server is not parametrized to allow HTTPS, some method will raise an "oauthlib.oauth2.rfc6749.errors.InsecureTransportError". Set this to `True` to avoid this error.
OAUTH_ENABLE_INSECURE_TRANSPORT = False

# # Client id/secret generated by authorization server when you register your client application.
OAUTH_CLIENT_ID = "{{ hostvars['authelia'].authelia_seafile_id }}"
OAUTH_CLIENT_SECRET = "{{ hostvars['authelia'].authelia_seafile_secret }}"

# # Callback url when user authentication succeeded. Note, the redirect url you input when you register your client application MUST be exactly the same as this value.
OAUTH_REDIRECT_URL = 'https://{{ public_address }}/oauth/callback/'

OAUTH_PROVIDER_DOMAIN = '{{ hostvars['authelia_server'].public_address }}'
OAUTH_PROVIDER = '{{ hostvars['authelia_server'].public_address }}'

OAUTH_AUTHORIZATION_URL = 'https://{{ hostvars['authelia_server'].public_address }}/api/oidc/authorization'
OAUTH_TOKEN_URL = 'https://{{ hostvars['authelia_server'].public_address }}/api/oidc/token'
OAUTH_USER_INFO_URL = 'https://{{ hostvars['authelia_server'].public_address }}/api/oidc/userinfo'
OAUTH_SCOPE = [ "openid", "profile", "email", ]
OAUTH_ATTRIBUTE_MAP = {
    "email": (True, "email"),
    "name": (False, "name"),
    "id": (False, "not used"),
    }

# Since 8.0.6 CE/PRO version.
# Url redirected to after user logout Seafile.
# Usually configured as Single Logout url.
### TODO: need to figure out what this should be set to
#LOGOUT_REDIRECT_URL = 'http{s}://www.example-url.com'

# Metadata server
{% if use_metadata_server is true %}
ENABLE_METADATA_MANAGEMENT = True
METADATA_SERVER_URL = 'http://seafile-md-server:8084'
{% else %}
# Metadata server disabled
{% endif %}

After leaving this alone for awhile, I’ve come back re-invigorated. I’m not sure how I fixed it, but here we are!

I removed a trailing comma from a section in seahub_settings.py:

Before:
OAUTH_SCOPE = [ “openid”, “profile”, “email”, ]

After:
OAUTH_SCOPE = [ “openid”, “profile”, “email” ]

I don’t think this is the fix - you have the same trailing comma, after all.

But, I also changed the OAUTH_ATTRIBUTE_MAP:

“email”: (True, “uid”),
“uid”: (True, “email”),
“name”: (False, “name”)

And dialled back Authelia’s configuration to basics:

  - client_id: 'seafile123'
    client_name: 'Seafile'
    client_secret: '$pbkdf2-sha512$310000$10QJVuo0RAC9K5bdi1qZ2w$32UiCqlotLyVBvUyesvTA8jYoiN4sGb1Sj9r>
    public: false
    authorization_policy: 'two_factor'
    require_pkce: false
    redirect_uris:
      - 'https://cloud.domain.com/oauth/callback/'
    scopes:
      - openid
      - profile
      - email

And… it seems to work. Now I just need to get attribute mapping like before so the user isn’t given “random_string@auth.local” as their email :sweat_smile:

Finally I can put this to rest, and move on to more fun things! Thanks for your input Tom!