Open rubydotexe opened 2 years ago
I think it has to do with my Caddy configuration? As you can see, I basically pulled things out of my bum and have no idea what I'm doing. I tried looking up examples for CORS in Caddy and I don't think I'm doing it right. If I'm doing something wrong (and I most certainly am) can someone provide an example of what it should look like?
@rubydotexe , thank you for the question! 👍
Here are some snippets relevant to your CORS configuration:
auth.example.com {
import tls_config
> import options
authenticate with myportal
root * /usr/share/caddy
file_server
}
(options) {
header Access-Control-Allow-Methods "POST, GET, OPTIONS"
@options {
method OPTIONS
}
respond @options 204
> import cors https://example.com
import cors https://www.example.com
import cors https://auth.example.com
import cors https://dnd.example.com
import cors https://files.example.com
import cors https://neko.example.com
import cors https://5etools.example.com
import cors https://wiki.example.com
import cors https://orcpub.example.com
}
(cors) {
@origin{args.0} header Origin {args.0}
header @origin{args.0} Access-Control-Allow-Origin "{args.0}"
header @origin{args.0} Vary Origin
}
Please provide output of the following command. Let's see what headers it returns.
curl -v http://auth.example.com
It would be very interesting how the options
and cors
imports unwrap.
Hello Mr. Greenburg! I appreciate your time very much.
This is my Caddyfile now:
{
order authenticate before respond
order authorize before basicauth
#acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
security {
oauth identity provider github {env.GITHUB_CLIENT_ID} {env.GITHUB_CLIENT_SECRET}
authentication portal myportal {
enable identity provider github
ui {
links {
"My Website" https://example.com icon "las la-star"
"My Identity" "/whoami" icon "las la-user"
}
password_recovery_enabled no
}
transform user {
match origin local
action add role authp/user
ui link "Portal Settings" /settings icon "las la-cog"
}
transform user {
match realm github
match sub github.com/rubydotexe
action add role authp/user
}
}
authorization policy users_policy {
set auth url https://auth.example.com:443/
allow roles authp/admin authp/user
acl rule {
comment allow users
match role authp/user
allow stop log info
}
acl rule {
comment default deny
match any
deny log warn
}
}
authorization policy admins_policy {
set auth url https://auth.example.com:443/
allow roles authp/admin authp/user
acl rule {
comment allow users
match role authp/user
allow stop log info
}
acl rule {
comment default deny
match any
deny log warn
}
}
}
}
(tls_config) {
tls {
dns googleclouddns {
gcp_project {env.GCP_PROJECT}
gcp_application_default {env.GCP_APPLICATION_DEFAULT}
}
}
}
(options) {
header Access-Control-Allow-Methods "POST, GET, OPTIONS"
@options {
method OPTIONS
}
respond @options 204
import cors https://example.com
import cors https://www.example.com
import cors https://auth.example.com
import cors https://dnd.example.com
import cors https://files.example.com
import cors https://neko.example.com
import cors https://5etools.example.com
import cors https://wiki.example.com
import cors https://orcpub.example.com
import cors https://cron.example.com
}
(cors) {
@origin{args.0} header Origin {args.0}
header @origin{args.0} Access-Control-Allow-Origin "{args.0}"
header @origin{args.0} Vary Origin
}
auth.example.com {
import tls_config
import options
authenticate with myportal
root * /usr/share/caddy
file_server
}
example.com {
import tls_config
import options
authorize with users_policy
reverse_proxy flame:5005
}
*.example.com {
import tls_config
import options
@dnd host dnd.example.com
handle @dnd {
authorize with users_policy
reverse_proxy foundry:30000
encode zstd gzip
}
@pub host orcpub.example.com
handle @dnd {
#authorize with users_policy
reverse_proxy orcpub:8890
route /homebrew.orcbrew {
root /homebrew.orcbrew /srv/orcpub
file_server
}
}
@cron host cron.example.com
handle @cron {
authorize with users_policy
reverse_proxy crontab-ui:8000
encode zstd gzip
}
@wiki host wiki.example.com
handle @wiki {
authorize with users_policy
reverse_proxy raneto:3000
}
@neko host neko.example.com
handle @neko {
authorize with users_policy
reverse_proxy neko:8080 {
header_up Host {host}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
}
}
@tools host 5etools.example.com
handle @tools {
authorize with users_policy
reverse_proxy 5etools:80
}
@files host files.example.com
handle @files {
authorize with users_policy
reverse_proxy filebrowser:80
}
# Fallback for otherwise unhandled domains
handle {
abort
}
}
And this is what I got in response to curl -v http://auth.example.com:
ruby@HOST:~$ curl -v http://auth.example.com
* Trying [omitted]:80...
* TCP_NODELAY set
* Connected to auth.example.com ([omitted]) port 80 (#0)
> GET / HTTP/1.1
> Host: auth.example.com
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 308 Permanent Redirect
< Connection: close
< Location: https://auth.example.com/
< Server: Caddy
< Date: Thu, 21 Apr 2022 13:03:00 GMT
< Content-Length: 0
<
* Closing connection 0
And just out of curiosity I curled HTTPS as well:
ruby@CYBORG:~$ curl -v https://auth.example.com
* Trying [omitted]:443...
* TCP_NODELAY set
* Connected to auth.example.com ([omitted]) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=auth.example.com
* start date: Apr 3 15:20:16 2022 GMT
* expire date: Jul 2 15:20:15 2022 GMT
* subjectAltName: host "auth.example.com" matched cert's "auth.example.com"
* issuer: C=US; O=Let's Encrypt; CN=R3
* SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x55726970b880)
> GET / HTTP/2
> Host: auth.example.com
> user-agent: curl/7.68.0
> accept: */*
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* Connection state changed (MAX_CONCURRENT_STREAMS == 250)!
< HTTP/2 302
< access-control-allow-methods: POST, GET, OPTIONS
< cache-control: no-store
< location: https://auth.example.com/login
< pragma: no-cache
< server: Caddy
< set-cookie: AUTHP_SESSION_ID=[omited]; Domain=example.com; Path=/; Secure; HttpOnly;
< content-length: 0
< date: Thu, 21 Apr 2022 13:03:09 GMT
<
* Connection #0 to host auth.example.com left intact
@rubydotexe , for testing, please do the following:
auth.example.com {
import tls_config
# import options
header Access-Control-Allow-Origin "*"
header Access-Control-Allow-Methods "*"
authenticate with myportal
root * /usr/share/caddy
file_server
}
Then curl
the following way:
curl -v -L https://auth.example.com
@greenpau Here you go:
* Trying [omited]:443...
* TCP_NODELAY set
* Connected to auth.example.com ([omited]) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=auth.example.com
* start date: Apr 3 15:20:16 2022 GMT
* expire date: Jul 2 15:20:15 2022 GMT
* subjectAltName: host "auth.example.com" matched cert's "auth.example.com"
* issuer: C=US; O=Let's Encrypt; CN=R3
* SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0xaaaabda25730)
> GET / HTTP/2
> Host: auth.example.com
> user-agent: curl/7.68.0
> accept: */*
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* Connection state changed (MAX_CONCURRENT_STREAMS == 250)!
< HTTP/2 302
< access-control-allow-methods: *
< access-control-allow-origin: *
< cache-control: no-store
< location: https://auth.example.com/login
< pragma: no-cache
< server: Caddy
< set-cookie: AUTHP_SESSION_ID=[omitted]; Domain=example.com; Path=/; Secure; HttpOnly;
< content-length: 0
< date: Fri, 22 Apr 2022 13:34:13 GMT
<
* Connection #0 to host auth.example.com left intact
* Issue another request to this URL: 'https://auth.example.com/login'
* Found bundle for host auth.example.com: 0xaaaabda19940 [can multiplex]
* Re-using existing connection! (#0) with host auth.example.com
* Connected to auth.example.com ([omited]) port 443 (#0)
* Using Stream ID: 3 (easy handle 0xaaaabda25730)
> GET /login HTTP/2
> Host: auth.example.com
> user-agent: curl/7.68.0
> accept: */*
>
< HTTP/2 200
< access-control-allow-methods: *
< access-control-allow-origin: *
< content-type: text/html
< server: Caddy
< set-cookie: AUTHP_SESSION_ID=[omitted]; Domain=example.com; Path=/; Secure; HttpOnly;
< content-length: 1856
< date: Fri, 22 Apr 2022 13:34:13 GMT
<
<!doctype html>
<html lang="en">
<head>
<title>Sign In</title>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="Authentication Portal">
<meta name="author" content="Paul Greenberg github.com/greenpau">
<link rel="shortcut icon" href="/assets/images/favicon.png" type="image/png">
<link rel="icon" href="/assets/images/favicon.png" type="image/png">
<!-- Matrialize CSS -->
<link rel="stylesheet" href="/assets/materialize-css/css/materialize.css" />
<link rel="stylesheet" href="/assets/google-webfonts/roboto.css" />
<link rel="stylesheet" href="/assets/line-awesome/line-awesome.css" />
<link rel="stylesheet" href="/assets/css/styles.css" />
</head>
<body class="app-body">
<div class="container">
<div class="row">
<div class="col s12 m8 offset-m2 l6 offset-l3 xl4 offset-xl4 app-card-container">
<div class="row app-header center">
<div class="col s4">
<img class="d-block mx-auto mb-2" src="/assets/images/logo.svg" alt="Authentication Portal" width="72" height="72">
</div>
<div class="col s8">
<h4>Sign In</h4>
</div>
</div>
<div class="row">
<a class="waves-effect waves-light grey darken-3 app-btn btn" href="oauth2/github">
<i class="lab la-github app-btn-icon"></i><span class="app-btn-text">Github</span>
</a>
</div>
</div>
</div>
</div>
<!-- Optional JavaScript -->
<script src="/assets/materialize-css/js/materialize.js"></script>
</body>
* Connection #0 to host auth.example.com left intact
Here you go:
@rubydotexe , do you still get Cross-Origin Request Blocked
?
Here you go:
@rubydotexe , do you still get
Cross-Origin Request Blocked
?
The console errors are at least slightly different, but I'm still having to login at short intervals.
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://github.com/login/oauth/authorize?client_id=[redacted]&redirect_uri=https%3A%2F%2Fauth.example.com%2Foauth2%2Fgithub%2Fauthorization-code-callback&scope=read%3Auser&state=[redacted]. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 302.
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://github.com/login/oauth/authorize?client_id=[redacted]&redirect_uri=https%3A%2F%2Fauth.example.com%2Foauth2%2Fgithub%2Fauthorization-code-callback&scope=read%3Auser&state=[redacted]. (Reason: CORS request did not succeed). Status code: (null).
The console errors are at least slightly different, but I'm still having to login at short intervals.
@rubydotexe , please use Chrome and collect logs (HAR) from your session. Then, email them to me.
@greenpau I have the exact same problem. I tried playing around with the CORS settings in my Caddyfile but to no avail. I have the HAR archive if you need it, what's your email ?
@LeonardMeyer , greenpau|outlook.com
Sent ! For context I have a Caddy container reverse proxying to subdomains pointing to several other containers .
@greenpau Actually trying your last suggestion got me forward. I added the Access-Control
headers one by one as I was told they were missing from the browser console. Then I ended up having some generic CORS error with a null
message like above. MDN ended up being pretty useful , especially the "Preflight requests and credentials" part, because apparently I was missing the Access-Control-Allow-Credentials: true
header. YMMV though because it seems very dependent on your browser, ad-blockers and your apps.
auth.example.com {
route {
header Access-Control-Allow-Origin "*"
header Access-Control-Allow-Methods "*"
header Access-Control-Allow-Headers "*"
header Access-Control-Allow-Credentials "true"
authenticate * with myportal
}
}
I'm not fond of using the wildcard so I'll try with more specific headers (which seems not so straightforward), but it seems to work.
I'm not fond of using the wildcard so I'll try with more specific headers (which seems not so straightforward), but it seems to work.
@LeonardMeyer , you don't have to use wilcards. They are for testing only. Now, you can set whatever domain you need it to be.
See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
Access-Control-Allow-Origin: https://developer.mozilla.org
@greenpau Nevermind, it looked better because I saw some preflight requests going through but it just took longer to fail. Browser console is saying things like:
Same origin policy disallows reading the remote resource https://example.com/?redirect_url=https%3A%2F%2Fsubdomain.example.com%2Fapi%2Fv1%2FindexerProxy. Reason: CORS request external redirect not allowed
So this was the first error and it matches that page. Origin of the preflight request was https://subdomain.example.com
and not https://subdomain.example.com/api/v3/command
, maybe that would explain that error ?
I tested in another app and what I see is this (I have the HAR if needed) :
1) Subdomain makes a request
GET /api/v1/indexerProxy HTTP/2
Host: subdomain.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:100.0) Gecko/20100101 Firefox/100.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br
X-Api-Key: <redacted>
X-Requested-With: XMLHttpRequest
DNT: 1
Connection: keep-alive
Referer: https://subdomain.example.com/settings/indexers
Cookie: AUTHP_SESSION_ID=<redacted>; AUTHP_REDIRECT_URL=https://subdomain.example.com/index.js.map
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
TE: trailers
2) Then for some reason server goes nope and respond with HTTP 302 to the auth url and CORSExternalRedirectNotAllowed is logged in console.
HTTP/2 302 Found
location: https://example.com?redirect_url=https%3A%2F%2Fsubdomain.example.com%2Fapi%2Fv1%2FindexerProxy
server: Caddy
content-type: text/plain; charset=utf-8
content-length: 5
date: Tue, 17 May 2022 12:38:02 GMT
X-Firefox-Spdy: h2
3) A preflight request is then issued because CORS:
OPTIONS /?redirect_url=https%3A%2F%2Fsubdomain. HTTP/2
Host: example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:100.0) Gecko/20100101 Firefox/100.0
Accept: */*
Accept-Language: fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br
Access-Control-Request-Method: GET
Access-Control-Request-Headers: x-api-key,x-requested-with
Referer: https://subdomain.example.com/
Origin: https://subdomain.example.com
DNT: 1
Connection: keep-alive
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
4) Preflight response is issued and CORSPreflightDidNotSucceed is logged in console.
HTTP/2 302 Found
access-control-allow-credentials: true
access-control-allow-headers: *
access-control-allow-methods: *
access-control-allow-origin: *
cache-control: no-store
location: https://example.com/login
pragma: no-cache
server: Caddy
set-cookie: AUTHP_SESSION_ID=<redacted>; Domain=example.stream; Path=/; Secure; HttpOnly;
content-length: 0
date: Tue, 17 May 2022 12:38:02 GMT
X-Firefox-Spdy: h2
@greenpau After some tinkering I ended up with the same trick to fool preflight request as @rubydotexe, drawing inspiration from this, in order to avoid the preflight requests and redirect mess.
This is my Caddyfile:
{
order authenticate before respond
security {
local identity store localdb {
realm local
path {$CADDY_AUTH_USERS_PATH}
}
authentication portal myportal {
enable identity store localdb
cookie domain {$DOMAIN}
cookie lifetime 43200
ui {
links {
"Sonarr" https://subdomain1.{$DOMAIN} icon "las la-star"
"Radarr" https://subdomain2.{$DOMAIN} icon "las la-star"
}
}
}
authorization policy admin_policy {
set auth url https://{$DOMAIN}
allow roles authp/admin
acl rule {
comment allow admins
match role authp/admin
allow stop log info
}
acl rule {
comment default deny
match any
deny log warn
}
}
}
}
(protected_route) {
{args.0}.{$DOMAIN} {
route {
authorize with admin_policy
reverse_proxy {args.1}
}
}
}
{$DOMAIN} {
route {
header Access-Control-Allow-Origin "*"
header Access-Control-Allow-Methods "*"
header Access-Control-Allow-Headers "*"
header Access-Control-Allow-Credentials "true"
header Access-Control-Max-Age 86400
@options method OPTIONS
handle @options {
header Content-Type "text/plain charset=UTF-8"
header Content-Length 0
respond 204
}
handle {
authenticate * with myportal
}
}
}
import protected_route subdomain1 subdomain1:8989
import protected_route subdomain2 subdomain2:7878
It might be bad, I'm pretty new at Caddy. With this I don't have any CORS errors anymore, but at some point it will trigger some random JS errors in the console specific to the app which failed. I can't make sense of the requests I'm seeing when one error occurs. See an example:
First request
GET https://subdomain1.example.com/api/v1/notification
Response
HTTP/2 302 Found
location: https://example.com/login?login_hint=webadmin%40localdomain.local&redirect_url=https%3A%2F%2Fsubdomain1.example.com%2Fapi%2Fv1%2Fnotification
server: Caddy
set-cookie: access_token=delete; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT
content-type: text/plain; charset=utf-8
content-length: 5
date: Tue, 17 May 2022 22:19:31 GMT
X-Firefox-Spdy: h2
Second request
OPTIONS https://example.com/login?login_hint=webadmin@localdomain.local&redirect_url=https://subdomain1.example.com/api/v1/notification
Response
HTTP/2 204 No Content
access-control-allow-credentials: true
access-control-allow-headers: *
access-control-allow-methods: *
access-control-allow-origin: *
access-control-max-age: 86400
content-type: text/plain charset=UTF-8
server: Caddy
content-length: 0
date: Tue, 17 May 2022 22:19:31 GMT
X-Firefox-Spdy: h2
Third request
GET https://example.com/login?login_hint=webadmin@localdomain.local&redirect_url=https://subdomain1.example.com/api/v1/notification
Response
HTTP/2 200 OK
access-control-allow-credentials: true
access-control-allow-headers: *
access-control-allow-methods: *
access-control-allow-origin: *
access-control-max-age: 86400
content-type: text/html
server: Caddy
set-cookie: AUTHP_SESSION_ID=<redacted>; Domain=example.com; Path=/; Secure; HttpOnly;
set-cookie: AUTHP_REDIRECT_URL=https://subdomain1.example.com/api/v1/notification; Domain=example.com; Path=/; Max-Age=43200; Secure; HttpOnly;
content-length: 2788
date: Tue, 17 May 2022 22:19:31 GMT
X-Firefox-Spdy: h2
I don't understand why the first request is answered 302 ? Is this a caddy-security thing ? It looks like something expired and I have to authenticate again ? Last response body is the login page HTML by the way. Following that some JS is crashing in the console and the page just fails loading the view.
I don't understand why the first request is answered 302 ?
I recommend creating two different routes. One for API endpoints and another one for non-API endpoint. In non-API (app) polict you would set the redirect to work. In API policy, you would return 403 only, i.e. no redirect. Here is an example.
authorization policy appPolicy {
set auth url /auth
crypto key verify CHANGE_ME
acl rule {
comment allow admins and users
match role authp/admin authp/user
allow stop log info
}
acl rule {
comment default deny
match any
deny log warn
}
}
authorization policy apiPolicy {
# do not redirect to /auth, return 403.
disable auth redirect
crypto key verify CHANGE_ME
acl rule {
comment allow admins and users
match role authp/admin authp/user
allow stop log info
}
acl rule {
comment default deny
match any
deny log warn
}
}
(protected_route) {
{args.0}.{$DOMAIN} {
route /api* {
authorize with apiPolicy
reverse_proxy {args.1}
}
route {
authorize with appPolicy
reverse_proxy {args.1}
}
}
}
Thanks for your answer @greenpau
/api*
because I have several subdomains and thus containers reverse proxied, and they could have any path. I could try /*
I suppose but getting a 401/403 isn't the goal here anyway. Can't I just check the authorization and let the request through ?@LeonardMeyer , i totally misunderstood the above. You get 302 because authorizer did not find token, or it is expired.
@greenpau Ok actually I just saw #24 and that was why it was expiring so fast. CORS issue are also solved (as in no console errors). There's just one minor thing that is bothering me... With my Caddyfile and what happens with the 3 request/response above when token expires, the redirection to login happens only if I refresh the page. Otherwise the current page just breaks but stays on.
There's just one minor thing that is bothering me... With my Caddyfile and what happens with the https://github.com/greenpau/caddy-security/issues/90#issuecomment-1129398616 above when token expires, the redirection to login happens only if I refresh the page. Otherwise the current page just breaks but stays on.
@LeonardMeyer , please elaborate. What is the desired behavior.
P.S. you totally hijacked the issue 😄 next time, open a new one and reference the issue that is similar to yours. Just a suggestion.
@greenpau My bad, I did have a CORS issue initially 😅
The desired behavior should be that the redirect actually work as soon as the token is expired. But maybe CORS and preflight request is messing with said redirect.
@LeonardMeyer , did you have a chance to review this https://github.com/greenpau/caddy-security/issues/24#issuecomment-1019633596? i.e. there is a difference between:
crypto default token lifetime ...
cookie lifetime ...
@greenpau Yes, as I said in my previous comment. Session lifespan is fine now.
Hello again!
This is the behavior I'm having trouble with:
Basically, when I am using FoundryVTT the connection to my assets disconnect until I sign in the Caddy Security portal again. Problem is that this happens frequently. When I refresh the tab, I am sent to the auth portal, I sign in, and the problem is resolved for about less then five minutes where then the cycle repeats.
I checked the JS console log:
I think it has to do with my Caddy configuration? As you can see, I basically pulled things out of my bum and have no idea what I'm doing. I tried looking up examples for CORS in Caddy and I don't think I'm doing it right. If I'm doing something wrong (and I most certainly am) can someone provide an example of what it should look like?