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.42k stars 70 forks source link

question: No Token Found for Discord auth, confused about config #296

Closed michael94ellis closed 10 months ago

michael94ellis commented 10 months ago

I'm trying to setup multiple subdomains with Discord auth using caddy-security on a debian 11 system.

I read over this page and many others in the docs several times before attempting this. I was excited to see the correct discord app appear.

My Problem - When I login and click authorize it just endlessly redirects me to the discord auth page. On caddy I see the following error without debug mode on:

 http.handlers.authentication    auth provider returned error    {"provider": "authorizer", "error": "user authorization failed: src_ip=192.168.1.1, src_conn_ip=192.168.1.1, reason: no token found"}

I also just added debug mode to my Caddyfile and retrieved these extra assoicated logs

DEBUG security        token validation error  {"session_id": "", "request_id": "b2f8de29-b439-4725-b264-68c63ce192d2", "error": "no token found"}
DEBUG security        redirecting unauthorized user   {"session_id": "", "request_id": "b2f8de29-b439-4725-b264-68c63ce192d2", "method": "location"}
ERROR  http.handlers.authentication    auth provider returned error    {"provider": "authorizer", "error": "user authorization failed: src_ip=192.168.1.1, src_conn_ip=192.168.1.1, reason: no token found"}
DEBUG http.log.error  not authenticated       
{"request": { "remote_ip": "192.168.1.1", "remote_port": "123456", "client_ip": "192.168.1.1", "proto": "HTTP/2.0", "method": "GET", 
"host": "second.domain.com", 
"uri": "/?code=ABC123abcABCabc123123",
 "headers":

 {"Upgrade-Insecure-Requests": ["1"], "Sec-Fetch-User": ["?1"], "Sec-Fetch-Dest": ["document"], "Cookie": [], "Sec-Ch-Ua": ["\"Google Chrome\";v=\"119\", \"Chromium\";v=\"119\", \"Not?A_Brand\";v=\"24\""], "User-Agent": ["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36"],
 "Referer": ["https://discord.com/"], "Sec-Ch-Ua-Platform": ["\"Windows\""],
 "Accept": ["text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"], "Accept-Language": ["en-US,en;q=0.9"], "Sec-Ch-Ua-Mobile": ["?0"], "Sec-Fetch-Site": ["cross-site"], "Sec-Fetch-Mode": ["navigate"], "Accept-Encoding": ["gzip, deflate, br"]},
 "tls": {"resumed": false, "version": 772, "cipher_suite": 4567, "proto": "h2", "server_name": "second.domain.com"}}, 
"duration": 0.001434217,
 "status": 401, "err_id": "mx2dk3rqp", 
"err_trace": "caddyauth.Authentication.ServeHTTP (caddyauth.go:89)"}

Here is my Caddyfile. I plan to have multiple authorization policies

        email 'mydomainemail@email.com'

        order authenticate before respond
        order authorize before basicauth

        security {
                oauth identity provider discord {
                        realm discord
                        driver discord
                        client_id CLIENTID
                        client_secret CLIENTSECRET
                        scopes identify guilds
                        user_group_filters 101010101
                }
                authentication portal discordportal {
                        crypto default token lifetime 3600
                        crypto key sign-verify CLIENTSECRET
                        enable identity provider discord
                        cookie domain *domain.com

                        ui {
                                links {
                                        "My Identity" "/whoami" icon "las la-user"
                                }
                        }

                        transform user { # admin
                                match sub discord.com/101010101/1111111
                                action add role authp/admin
                        }
                        transform user { 
                                match sub discord.com/101010101/222222222
                                action add role authp/superuser
                        }
                        transform user { 
                                match sub discord.com/101010101/333333333
                                action add role authp/poweruser
                        }
                        transform user 
                                match sub discord.com/101010101/4444444444
                                action add role authp/user
                        }
                }
                authorization policy firstpolicy {
                        set auth url https://discord.com/api/oauth2/authorize?client_id=CLIENTID&redirect_uri=https%3A%2F%2Ffirst.domain.com&response_type=code&scope=identify%20guilds
                        crypto key verify CLIENTSECRET
                        allow roles authp/admin authp/superuser authp/poweruser authp/user
                        validate bearer header
                        inject headers with claims
                }
                authorization policy secondpolicy {
                        set auth url https://discord.com/api/oauth2/authorize?client_id=CLIENTID&redirect_uri=https%3A%2F%2Fsecond.domain.com&response_type=code&scope=identify%20guilds
                        crypto key verify CLIENTSECRET
                        allow roles authp/admin authp/superuser authp/poweruser
                        validate bearer header
                        inject headers with claims
                }
        }
}

https://first.domain.com {
        tls mydomainemail@email.com
        authenticate with discordportal
        authorize with firstpolicy
        reverse_proxy 192.168.1.200:8096
}
https://second.domain.com {
        tls mydomainemail@email.com
        authenticate with discordportal
        authorize with secondpolicy
        reverse_proxy 192.168.1.133:1234
}

My primary questions are:

Also

Thanks in advance!

greenpau commented 10 months ago

Should I be using the discord auth app's CLIENTSECRET for the crypto key verify-sign values in the policy?

@michael94ellis , no. The crypto key verify-sign related to the token issues by the plugin.

Is this infinite redirect loop for clicking "Authorize" on the discord auth page because of incorrect config? How can I make sure the token can be found and resolve the error?

Fix your authorization.

Does my Caddyfile look correct? I tried my best to compare to google/github/gitlab/generic

It does not.

https://second.domain.com {
        tls mydomainemail@email.com
        authenticate with discordportal
        authorize with secondpolicy
        reverse_proxy 192.168.1.133:1234
}

I think you misunderstood how caddy routing works.

See configs here: https://github.com/authp/authp.github.io/tree/main/assets/conf/oauth

You probably need something like this:

https://second.domain.com {
        tls mydomainemail@email.com

    route /auth/* {
        authenticate with discordportal
        }
        route {
          authorize with secondpolicy
        reverse_proxy 192.168.1.133:1234
      }
}

This one is wrong too.

                authorization policy firstpolicy {
                        set auth url https://discord.com/api/oauth2/authorize?client_id=CLIENTID&redirect_uri=https%3A%2F%2Ffirst.domain.com&response_type=code&scope=identify%20guilds
                        crypto key verify CLIENTSECRET
                        allow roles authp/admin authp/superuser authp/poweruser authp/user
                        validate bearer header
                        inject headers with claims
                }

The URL here refers to the plugin's authentication URL.

                authorization policy firstpolicy {
                        set auth url https://second.domain.com/auth/
                        crypto key verify CLIENTSECRET
                        allow roles authp/admin authp/superuser authp/poweruser authp/user
                        validate bearer header
                        inject headers with claims
                }
michael94ellis commented 10 months ago

Thank you very much, those corrections were very helpful! I've progressed to a new error which I have been fiddling with for the past few hours. I've pasted in the result of incorporating your feedback below for reference.

Unfortunately I am unable to retrieve the roles and properly transform the user as per the Discord Guild/Server roles. I tried using the string value of the role and the id as well. I also attempted to intercept and decode the JWT where I found no role information.

I then returned to investigating in the docs. I tried adding the scope guilds.members.read according to Discord Oauth2 Docs. I then looked further into where the request is made from caddy-security and noticed that there seems to be a url difference from caddy security's request url "discord.com/%s/members", guildID compared to the path in the Discord Oauth2 docs /users/@me/guilds/{guild.id}/member

To be honest, I'm in over my head. Do you think my user transform is wrong?

{
        email 'mydomainemail@email.com'

        order authenticate before respond
        order authorize before basicauth

        security {
                oauth identity provider discord {
                        realm discord
                        driver discord
                        client_id {$DISCORDCLIENTID}
                        client_secret {$DISCORDSECRET}
                        scopes identify guilds
                        user_group_filters {$MYGUILDID}
                }
                authentication portal discordportal {
                        crypto default token lifetime 3600
                        crypto key sign-verify {env.JWT_SHARED_KEY}
                        enable identity provider discord
                        cookie domain *.domain.com

                        transform user {
                                match realm discord
                                match role discord.com/{$MYGUILDID}/{$ADMINROLEID}
                                action add role authp/admin
                        }
                        transform user {
                                match sub discord.com/{$MYGUILDID}/{$MEMBERROLEID}
                                action add role authp/user
                        }
                }
                authorization policy firstpolicy {
                        set auth url https://first.domain.com/auth/
                        crypto key verify {env.JWT_SHARED_KEY}
                        allow roles authp/admin authp/user
                        validate bearer header
                        inject headers with claims
                }
        }
}
https://first.domain.com {
        tls mydomainemail@email.com
        route /auth/* {
                authenticate with discordportal
        }
        route {
                authorize with firstpolicy
                respond "Hello World"
        }
}
greenpau commented 10 months ago

@michael94ellis , what happens when you have only the below transform? Do you get authenticated? Can you browse to the portal?

                        transform user {
                                match realm discord
                                action add role authp/user
                        }

Also, remove the following:

                        user_group_filters {$MYGUILDID}

Please connect to me over the linkedin. Let's schedule google meet and troubleshoot together.

michael94ellis commented 10 months ago

I did attempt using the following transform alongside 1 or 3 of the other transforms I want to use. This did work, and I believe I could verify the users guild and user id as well.

               transform user {
                                match realm discord
                                action add role authp/user
               }

I also tried a few combinations for matching roles. I think the debug logs included an unsigned copy of my JWT in some response which when decoded had no roles, but it did have the appropriate list of guilds.

As for scheduling a debug session, that would be awesome. I will do that

michael94ellis commented 10 months ago

For any future readers, I made 2 MRs to add Guild Role based auth to extend the current caddy-security Discord offering

Documentation - https://github.com/authp/authp.github.io/pull/53 Auth Code - https://github.com/greenpau/go-authcrunch/pull/50

greenpau commented 6 months ago

@michael94ellis, I am looking to add testimonial sections to https://authcrunch.com. Could you please write one and send it to me at greenpau@outlook.com?