+1
-2
appview/config.go
+1
-2
appview/config.go
+22
-22
appview/oauth/handler/handler.go
+22
-22
appview/oauth/handler/handler.go
···
51
51
}
52
52
53
53
func (o *OAuthHandler) clientMetadata(w http.ResponseWriter, r *http.Request) {
54
-
metadata := map[string]any{
55
-
"client_id": o.Config.OAuth.ServerMetadataUrl,
56
-
"client_name": "Tangled",
57
-
"subject_type": "public",
58
-
"client_uri": o.Config.Core.AppviewHost,
59
-
"redirect_uris": []string{fmt.Sprintf("%s/oauth/callback", o.Config.Core.AppviewHost)},
60
-
"grant_types": []string{"authorization_code", "refresh_token"},
61
-
"response_types": []string{"code"},
62
-
"application_type": "web",
63
-
"dpop_bound_access_tokens": true,
64
-
"jwks_uri": fmt.Sprintf("%s/oauth/jwks.json", o.Config.Core.AppviewHost),
65
-
"scope": "atproto transition:generic",
66
-
"token_endpoint_auth_method": "private_key_jwt",
67
-
"token_endpoint_auth_signing_alg": "ES256",
68
-
}
69
-
70
54
w.Header().Set("Content-Type", "application/json")
71
55
w.WriteHeader(http.StatusOK)
72
-
json.NewEncoder(w).Encode(metadata)
56
+
json.NewEncoder(w).Encode(o.OAuth.ClientMetadata())
73
57
}
74
58
75
59
func (o *OAuthHandler) jwks(w http.ResponseWriter, r *http.Request) {
···
101
85
o.Pages.Notice(w, "login-msg", fmt.Sprintf("\"%s\" is an invalid handle.", handle))
102
86
return
103
87
}
88
+
self := o.OAuth.ClientMetadata()
104
89
oauthClient, err := client.NewClient(
105
-
o.Config.OAuth.ServerMetadataUrl,
90
+
self.ClientID,
106
91
o.Config.OAuth.Jwks,
107
-
fmt.Sprintf("%s/oauth/callback", o.Config.Core.AppviewHost))
92
+
self.RedirectURIs[0],
93
+
)
108
94
109
95
if err != nil {
110
96
log.Println("failed to create oauth client:", err)
···
164
150
}
165
151
166
152
u, _ := url.Parse(authMeta.AuthorizationEndpoint)
167
-
u.RawQuery = fmt.Sprintf("client_id=%s&request_uri=%s", url.QueryEscape(o.Config.OAuth.ServerMetadataUrl), parResp.RequestUri)
153
+
query := url.Values{}
154
+
query.Add("client_id", self.ClientID)
155
+
query.Add("request_uri", parResp.RequestUri)
156
+
u.RawQuery = query.Encode()
168
157
o.Pages.HxRedirect(w, u.String())
169
158
}
170
159
}
···
186
175
}
187
176
}()
188
177
178
+
error := r.FormValue("error")
179
+
errorDescription := r.FormValue("error_description")
180
+
if error != "" || errorDescription != "" {
181
+
log.Printf("error: %s, %s", error, errorDescription)
182
+
o.Pages.Notice(w, "login-msg", "Failed to authenticate. Try again later.")
183
+
return
184
+
}
185
+
189
186
code := r.FormValue("code")
190
187
if code == "" {
191
188
log.Println("missing code for state: ", state)
···
200
197
return
201
198
}
202
199
200
+
self := o.OAuth.ClientMetadata()
201
+
203
202
oauthClient, err := client.NewClient(
204
-
o.Config.OAuth.ServerMetadataUrl,
203
+
self.ClientID,
205
204
o.Config.OAuth.Jwks,
206
-
fmt.Sprintf("%s/oauth/callback", o.Config.Core.AppviewHost))
205
+
self.RedirectURIs[0],
206
+
)
207
207
208
208
if err != nil {
209
209
log.Println("failed to create oauth client:", err)
+62
-2
appview/oauth/oauth.go
+62
-2
appview/oauth/oauth.go
···
4
4
"fmt"
5
5
"log"
6
6
"net/http"
7
+
"net/url"
7
8
"time"
8
9
9
10
"github.com/gorilla/sessions"
···
113
114
if err != nil {
114
115
return nil, false, err
115
116
}
116
-
oauthClient, err := client.NewClient(o.Config.OAuth.ServerMetadataUrl,
117
+
118
+
self := o.ClientMetadata()
119
+
120
+
oauthClient, err := client.NewClient(
121
+
self.ClientID,
117
122
o.Config.OAuth.Jwks,
118
-
fmt.Sprintf("%s/oauth/callback", o.Config.Core.AppviewHost))
123
+
self.RedirectURIs[0],
124
+
)
119
125
120
126
if err != nil {
121
127
return nil, false, err
···
206
212
207
213
return xrpcClient, nil
208
214
}
215
+
216
+
type ClientMetadata struct {
217
+
ClientID string `json:"client_id"`
218
+
ClientName string `json:"client_name"`
219
+
SubjectType string `json:"subject_type"`
220
+
ClientURI string `json:"client_uri"`
221
+
RedirectURIs []string `json:"redirect_uris"`
222
+
GrantTypes []string `json:"grant_types"`
223
+
ResponseTypes []string `json:"response_types"`
224
+
ApplicationType string `json:"application_type"`
225
+
DpopBoundAccessTokens bool `json:"dpop_bound_access_tokens"`
226
+
JwksURI string `json:"jwks_uri"`
227
+
Scope string `json:"scope"`
228
+
TokenEndpointAuthMethod string `json:"token_endpoint_auth_method"`
229
+
TokenEndpointAuthSigningAlg string `json:"token_endpoint_auth_signing_alg"`
230
+
}
231
+
232
+
func (o *OAuth) ClientMetadata() ClientMetadata {
233
+
makeRedirectURIs := func(c string) []string {
234
+
return []string{fmt.Sprintf("%s/oauth/callback", c)}
235
+
}
236
+
237
+
clientURI := o.Config.Core.AppviewHost
238
+
clientID := fmt.Sprintf("%s/oauth/client-metadata.json", clientURI)
239
+
redirectURIs := makeRedirectURIs(clientURI)
240
+
241
+
if o.Config.Core.Dev {
242
+
clientURI = fmt.Sprintf("http://127.0.0.1:3000")
243
+
redirectURIs = makeRedirectURIs(clientURI)
244
+
245
+
query := url.Values{}
246
+
query.Add("redirect_uri", redirectURIs[0])
247
+
query.Add("scope", "atproto transition:generic")
248
+
clientID = fmt.Sprintf("http://localhost?%s", query.Encode())
249
+
}
250
+
251
+
jwksURI := fmt.Sprintf("%s/oauth/jwks.json", clientURI)
252
+
253
+
return ClientMetadata{
254
+
ClientID: clientID,
255
+
ClientName: "Tangled",
256
+
SubjectType: "public",
257
+
ClientURI: clientURI,
258
+
RedirectURIs: redirectURIs,
259
+
GrantTypes: []string{"authorization_code", "refresh_token"},
260
+
ResponseTypes: []string{"code"},
261
+
ApplicationType: "web",
262
+
DpopBoundAccessTokens: true,
263
+
JwksURI: jwksURI,
264
+
Scope: "atproto transition:generic",
265
+
TokenEndpointAuthMethod: "private_key_jwt",
266
+
TokenEndpointAuthSigningAlg: "ES256",
267
+
}
268
+
}
+4
-4
flake.lock
+4
-4
flake.lock
···
64
64
"inter-fonts-src": {
65
65
"flake": false,
66
66
"locked": {
67
-
"lastModified": 1731705360,
67
+
"lastModified": 1731687360,
68
68
"narHash": "sha256-5vdKKvHAeZi6igrfpbOdhZlDX2/5+UvzlnCQV6DdqoQ=",
69
69
"type": "tarball",
70
70
"url": "https://github.com/rsms/inter/releases/download/v4.1/Inter-4.1.zip"
···
89
89
},
90
90
"nixpkgs": {
91
91
"locked": {
92
-
"lastModified": 1746663147,
93
-
"narHash": "sha256-Ua0drDHawlzNqJnclTJGf87dBmaO/tn7iZ+TCkTRpRc=",
92
+
"lastModified": 1746904237,
93
+
"narHash": "sha256-3e+AVBczosP5dCLQmMoMEogM57gmZ2qrVSrmq9aResQ=",
94
94
"owner": "nixos",
95
95
"repo": "nixpkgs",
96
-
"rev": "dda3dcd3fe03e991015e9a74b22d35950f264a54",
96
+
"rev": "d89fc19e405cb2d55ce7cc114356846a0ee5e956",
97
97
"type": "github"
98
98
},
99
99
"original": {
+1
-1
flake.nix
+1
-1
flake.nix
···
171
171
air-watcher = name:
172
172
pkgs.writeShellScriptBin "run"
173
173
''
174
-
TANGLED_DEV=true ${pkgs.air}/bin/air -c /dev/null \
174
+
${pkgs.air}/bin/air -c /dev/null \
175
175
-build.cmd "${pkgs.go}/bin/go build -o ./out/${name}.out ./cmd/${name}/main.go" \
176
176
-build.bin "./out/${name}.out" \
177
177
-build.stop_on_error "true" \