forked from tangled.org/core
this repo has no description

appview: oauth: add dev configuration

setting TANGLED_DEV=true now lets you work on tangled without creating
ngrok/localtunnel tunnels.

Changed files
+90 -31
appview
oauth
+1 -2
appview/config.go
···
}
type OAuthConfig struct {
-
Jwks string `env:"JWKS"`
-
ServerMetadataUrl string `env:"SERVER_METADATA_URL"`
+
Jwks string `env:"JWKS"`
}
type JetstreamConfig struct {
+22 -22
appview/oauth/handler/handler.go
···
}
func (o *OAuthHandler) clientMetadata(w http.ResponseWriter, r *http.Request) {
-
metadata := map[string]any{
-
"client_id": o.Config.OAuth.ServerMetadataUrl,
-
"client_name": "Tangled",
-
"subject_type": "public",
-
"client_uri": o.Config.Core.AppviewHost,
-
"redirect_uris": []string{fmt.Sprintf("%s/oauth/callback", o.Config.Core.AppviewHost)},
-
"grant_types": []string{"authorization_code", "refresh_token"},
-
"response_types": []string{"code"},
-
"application_type": "web",
-
"dpop_bound_access_tokens": true,
-
"jwks_uri": fmt.Sprintf("%s/oauth/jwks.json", o.Config.Core.AppviewHost),
-
"scope": "atproto transition:generic",
-
"token_endpoint_auth_method": "private_key_jwt",
-
"token_endpoint_auth_signing_alg": "ES256",
-
}
-
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
-
json.NewEncoder(w).Encode(metadata)
+
json.NewEncoder(w).Encode(o.OAuth.ClientMetadata())
}
func (o *OAuthHandler) jwks(w http.ResponseWriter, r *http.Request) {
···
o.Pages.Notice(w, "login-msg", fmt.Sprintf("\"%s\" is an invalid handle.", handle))
return
}
+
self := o.OAuth.ClientMetadata()
oauthClient, err := client.NewClient(
-
o.Config.OAuth.ServerMetadataUrl,
+
self.ClientID,
o.Config.OAuth.Jwks,
-
fmt.Sprintf("%s/oauth/callback", o.Config.Core.AppviewHost))
+
self.RedirectURIs[0],
+
)
if err != nil {
log.Println("failed to create oauth client:", err)
···
}
u, _ := url.Parse(authMeta.AuthorizationEndpoint)
-
u.RawQuery = fmt.Sprintf("client_id=%s&request_uri=%s", url.QueryEscape(o.Config.OAuth.ServerMetadataUrl), parResp.RequestUri)
+
query := url.Values{}
+
query.Add("client_id", self.ClientID)
+
query.Add("request_uri", parResp.RequestUri)
+
u.RawQuery = query.Encode()
o.Pages.HxRedirect(w, u.String())
}
}
···
}
}()
+
error := r.FormValue("error")
+
errorDescription := r.FormValue("error_description")
+
if error != "" || errorDescription != "" {
+
log.Printf("error: %s, %s", error, errorDescription)
+
o.Pages.Notice(w, "login-msg", "Failed to authenticate. Try again later.")
+
return
+
}
+
code := r.FormValue("code")
if code == "" {
log.Println("missing code for state: ", state)
···
return
}
+
self := o.OAuth.ClientMetadata()
+
oauthClient, err := client.NewClient(
-
o.Config.OAuth.ServerMetadataUrl,
+
self.ClientID,
o.Config.OAuth.Jwks,
-
fmt.Sprintf("%s/oauth/callback", o.Config.Core.AppviewHost))
+
self.RedirectURIs[0],
+
)
if err != nil {
log.Println("failed to create oauth client:", err)
+62 -2
appview/oauth/oauth.go
···
"fmt"
"log"
"net/http"
+
"net/url"
"time"
"github.com/gorilla/sessions"
···
if err != nil {
return nil, false, err
}
-
oauthClient, err := client.NewClient(o.Config.OAuth.ServerMetadataUrl,
+
+
self := o.ClientMetadata()
+
+
oauthClient, err := client.NewClient(
+
self.ClientID,
o.Config.OAuth.Jwks,
-
fmt.Sprintf("%s/oauth/callback", o.Config.Core.AppviewHost))
+
self.RedirectURIs[0],
+
)
if err != nil {
return nil, false, err
···
return xrpcClient, nil
}
+
+
type ClientMetadata struct {
+
ClientID string `json:"client_id"`
+
ClientName string `json:"client_name"`
+
SubjectType string `json:"subject_type"`
+
ClientURI string `json:"client_uri"`
+
RedirectURIs []string `json:"redirect_uris"`
+
GrantTypes []string `json:"grant_types"`
+
ResponseTypes []string `json:"response_types"`
+
ApplicationType string `json:"application_type"`
+
DpopBoundAccessTokens bool `json:"dpop_bound_access_tokens"`
+
JwksURI string `json:"jwks_uri"`
+
Scope string `json:"scope"`
+
TokenEndpointAuthMethod string `json:"token_endpoint_auth_method"`
+
TokenEndpointAuthSigningAlg string `json:"token_endpoint_auth_signing_alg"`
+
}
+
+
func (o *OAuth) ClientMetadata() ClientMetadata {
+
makeRedirectURIs := func(c string) []string {
+
return []string{fmt.Sprintf("%s/oauth/callback", c)}
+
}
+
+
clientURI := o.Config.Core.AppviewHost
+
clientID := fmt.Sprintf("%s/oauth/client-metadata.json", clientURI)
+
redirectURIs := makeRedirectURIs(clientURI)
+
+
if o.Config.Core.Dev {
+
clientURI = fmt.Sprintf("http://127.0.0.1:3000")
+
redirectURIs = makeRedirectURIs(clientURI)
+
+
query := url.Values{}
+
query.Add("redirect_uri", redirectURIs[0])
+
query.Add("scope", "atproto transition:generic")
+
clientID = fmt.Sprintf("http://localhost?%s", query.Encode())
+
}
+
+
jwksURI := fmt.Sprintf("%s/oauth/jwks.json", clientURI)
+
+
return ClientMetadata{
+
ClientID: clientID,
+
ClientName: "Tangled",
+
SubjectType: "public",
+
ClientURI: clientURI,
+
RedirectURIs: redirectURIs,
+
GrantTypes: []string{"authorization_code", "refresh_token"},
+
ResponseTypes: []string{"code"},
+
ApplicationType: "web",
+
DpopBoundAccessTokens: true,
+
JwksURI: jwksURI,
+
Scope: "atproto transition:generic",
+
TokenEndpointAuthMethod: "private_key_jwt",
+
TokenEndpointAuthSigningAlg: "ES256",
+
}
+
}
+4 -4
flake.lock
···
"inter-fonts-src": {
"flake": false,
"locked": {
-
"lastModified": 1731705360,
+
"lastModified": 1731687360,
"narHash": "sha256-5vdKKvHAeZi6igrfpbOdhZlDX2/5+UvzlnCQV6DdqoQ=",
"type": "tarball",
"url": "https://github.com/rsms/inter/releases/download/v4.1/Inter-4.1.zip"
···
},
"nixpkgs": {
"locked": {
-
"lastModified": 1746663147,
-
"narHash": "sha256-Ua0drDHawlzNqJnclTJGf87dBmaO/tn7iZ+TCkTRpRc=",
+
"lastModified": 1746904237,
+
"narHash": "sha256-3e+AVBczosP5dCLQmMoMEogM57gmZ2qrVSrmq9aResQ=",
"owner": "nixos",
"repo": "nixpkgs",
-
"rev": "dda3dcd3fe03e991015e9a74b22d35950f264a54",
+
"rev": "d89fc19e405cb2d55ce7cc114356846a0ee5e956",
"type": "github"
},
"original": {
+1 -1
flake.nix
···
air-watcher = name:
pkgs.writeShellScriptBin "run"
''
-
TANGLED_DEV=true ${pkgs.air}/bin/air -c /dev/null \
+
${pkgs.air}/bin/air -c /dev/null \
-build.cmd "${pkgs.go}/bin/go build -o ./out/${name}.out ./cmd/${name}/main.go" \
-build.bin "./out/${name}.out" \
-build.stop_on_error "true" \