greenpau / caddy-security

🔐 Authentication, Authorization, and Accounting (AAA) App and Plugin for Caddy v2. 💎 Implements Form-Based, Basic, Local, LDAP, OpenID Connect, OAuth 2.0 (Github, Google, Facebook, Okta, etc.), SAML Authentication. MFA/2FA with App Authenticators and Yubico. 💎 Authorization with JWT/PASETO tokens. 🔐
https://authcrunch.com/
Apache License 2.0
1.43k stars 70 forks source link

question: How do I configure Discord as an identity provider #143

Open jaeichel opened 2 years ago

jaeichel commented 2 years ago

Is there a way to configure an identity provider for discord? I've tried using the caddy-security module (works great with Google and SimpleLogin), but Discord doesn't support a metadata_url and I don't know how to configure a generic driver without it.

From the Developer Portal, https://discord.com/developers/docs/topics/oauth2,

https://discord.com/api/oauth2/authorize | Base authorization URL https://discord.com/api/oauth2/token | Token URL https://discord.com/api/oauth2/token/revoke | Token Revocation URL

As far as I can tell on the forums, Discord does not implement a metadata_url, .well-known/openid-configuration path.

Is there a way to use OAuth 2.0 without the OpenID Connect part for Discord? My ultimate goal is to filter users based on if they are in my discord server.

Thank you!

greenpau commented 2 years ago

Is there a way to use OAuth 2.0 without the OpenID Connect part for Discord?

@jaeichel, it requires testing and, potentially, some development.

I do think that it is doable. Just registered with the dev portal, created an app and reviewed the app settings.

jaeichel commented 2 years ago

Cool and thanks. Really the part I'm stuck on is that I can't get past the requirement for a metadata_url in my Caddyfile. I can also help and dive in the code during the week if that helps.

greenpau commented 2 years ago

@jaeichel , for starters, try using “generic” driver in your config and manipulate options there.

https://github.com/authp/authp.github.io/blob/main/assets/conf/oauth/generic/Caddyfile

the options you could pass are here

https://github.com/greenpau/caddy-security/blob/0e5ffe0595d9622e83c33a11246f9558d4cd2359/caddyfile_identity_provider.go#L33

https://github.com/greenpau/go-authcrunch/blob/2af994b5ea87326b516bf6c4a373ea304a6fa49d/pkg/idp/oauth/config.go#L29

jaeichel commented 2 years ago

I've tried a generic driver with a few different combinations, but no luck.

I have a working Caddyfile with a generic driver for SimpleLogin (see Caddyfile below) to make sure everything else is up and running.

Then I tried a number of variations with Discord and the generic interface (see Caddyfile below). It feels like I'm getting closer, as I can complete the OAuth access token generation, but there are a few issues.

I'm going to try and see if I can hack a Discord OpenID Connect stub on my caddy server by providing a static, locally hosted .well-known/openid-configuration file that returns a userinfo_endpoint, which points to a locally hosted endpoint that requests https://discord.com/api/users/@me and maps return values into the openid format

Working SimpleLogin Generic Provider Example - CaddyFile

    https_port 443

    order authenticate before respond
    order authorize before basicauth

    security {
        oauth identity provider simple-login {
            realm simple-login
            driver generic
            client_id CLIENT_ID
            client_secret CLIENT_SECRET
            scopes openid 
            icon "SimpleLogin" "sign-in-alt" "pink darken-4"
            metadata_url https://app.simplelogin.io/.well-known/openid-configuration
        }

        authentication portal myportal {
            crypto default token lifetime 3600
            enable identity provider simple-login
            cookie domain mydomain.com
            ui {
                links {
                    "My Identity" "/whoami" icon "las la-user"
                }
            }

            transform user {
                match realm simple-login
                match email uuid@simplelogin.com
                action add role authp/user
            }
        }

        authorization policy mypolicy-simple-login {
            set auth url https://auth.mydomain.com/oauth2/simple-login
            allow roles authp/admin authp/user
            validate bearer header
            inject headers with claims
        }
    }
}

auth.mydomain.com {
    authenticate with myportal
}

mydomain.com {
    authorize with mypolicy-simple-login
    reverse_proxy 127.0.0.1:30000
}

Broken Discord Generic Provider Example - CaddyFile

{
    https_port 443

    order authenticate before respond
    order authorize before basicauth

    security {
        oauth identity provider discord {
            realm discord
            driver generic
            client_id CLIENT_ID
            client_secret CLIENT_SECRET
            scopes identify email guilds
            base_auth_url https://discord.com/api/oauth2
            authorization_url https://discord.com/api/oauth2/authorize
            token_url https://discord.com/api/oauth2/token

            # NOTE: Looks like metadata_url is required for config schema verification,
            # but not used if the above urls are present. A dummy value is implemented here instead
            metadata_url https://not-implemented-for-discord.com/.well-known/openid-configuration

            # NOTE: I cannot find a way to set the userinfo_endpoint, not implemented?
            # userinfo_endpoint https://discord.com/api/users/@me

            disable metadata_discovery
            disable key_verification
        }

        authentication portal myportal {
            crypto default token lifetime 3600
            enable identity provider discord
            cookie domain mydomain.com
            ui {
                links {
                    "My Identity" "/whoami" icon "las la-user"
                }
            }

            transform user {
                match realm discord
                match email uuid@simplelogin.com
                action add role authp/user
            }
        }

        authorization policy mypolicy-discord {
            set auth url https://auth.mydomain.com/oauth2/discord
            allow roles authp/admin authp/user
            validate bearer header
            inject headers with claims
        }
    }
}

auth.mydomain.com {
    authenticate with myportal
}

mydomain.com {
    authorize with mypolicy-discord
    reverse_proxy 127.0.0.1:30000
}
jaeichel commented 2 years ago

Quick follow-up, I did a quick python twisted server thinking that the userinfo_endpoint would be called before the id_token is created, but a valid id_token is required first. I think I might need to get caddy dev environment setup and learn a bit of go.

Where would be the best place in the code to start? I think the request is coming in on the callback, but doesn't include the id_token. Discord doesn't support the "openid" scope, so I was trying to use the "identify email" scope, which will require a hook to map the return into an openid profile.

greenpau commented 2 years ago

@jaeichel , dev environment setup instructions are here https://github.com/greenpau/caddy-security/blob/main/CONTRIBUTING.md#development-environment