···2626 Server *indieauth.Server
2727}
28282929-// storeAuthorization stores the authorization request and returns a code for it.
3030-// Something such as JWT tokens could be used in a production environment.
3129func (i *IndieAuth) storeAuthorization(req *indieauth.AuthenticationRequest) string {
3230 code := nanoid.New()
3331···4745 scopesContextKey contextKey = "scopes"
4846)
49475050-// HandleAuthGET handles the GET method for the authorization endpoint.
5148func (i *IndieAuth) HandleAuthGET(w http.ResponseWriter, r *http.Request) {
5252- // In a production server, this page would usually be protected. In order for
5353- // the user to authorize this request, they must be authenticated. This could
5454- // be done in different ways: username/password, passkeys, etc.
5555-5656- // Parse the authorization request.
5749 req, err := i.Server.ParseAuthorization(r)
5850 if err != nil {
5951 http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
6052 return
6153 }
62546363- // Do a best effort attempt at fetching more information about the application
6464- // that we can show to the user. Not all applications provide this sort of
6565- // information.
6655 app, _ := i.Server.DiscoverApplicationMetadata(r.Context(), req.ClientID)
67566868- // Here, we just display a small HTML document where the user has to press
6969- // to authorize this request. Please note that this template contains a form
7070- // where we dump all the request information. This makes it possible to reuse
7171- // [indieauth.Server.ParseAuthorization] when the user authorizes the request.
7272- layouts.RenderDefault(pages.Auth(req, app)).ServeHTTP(w, r)
5757+ nonceId, nonce := nanoid.New(), nanoid.New()
5858+ storage.NonceCache().Set(nonceId, nonce, 0)
5959+6060+ layouts.RenderDefault(pages.Auth(req, app, nonceId, nonce)).ServeHTTP(w, r)
7361}
74627575-// HandleAuthPOST handles the POST method for the authorization endpoint.
7663func (i *IndieAuth) HandleAuthPOST(w http.ResponseWriter, r *http.Request) {
7764 i.authorizationCodeExchange(w, r, false)
7865}
79668080-// HandleToken handles the token endpoint. In our case, we only accept the default
8181-// type which is exchanging an authorization code for a token.
8267func (i *IndieAuth) HandleToken(w http.ResponseWriter, r *http.Request) {
8368 if r.Method != http.MethodPost {
8469 http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
···10388 ExpiresIn int64 `json:"expires_in,omitempty"`
10489}
10590106106-// authorizationCodeExchange handles the authorization code exchange. It is used by
107107-// both the authorization handler to exchange the code for the user's profile URL,
108108-// and by the token endpoint, to exchange the code by a token.
10991func (i *IndieAuth) authorizationCodeExchange(w http.ResponseWriter, r *http.Request, withToken bool) {
11092 if err := r.ParseForm(); err != nil {
11193 SendErrorJSON(w, http.StatusBadRequest, "invalid_request", err.Error())
···163145}
164146165147func (i *IndieAuth) HandleAuthApproval(w http.ResponseWriter, r *http.Request) {
166166- // Parse authorization information. This only works because our authorization page
167167- // includes all the required information. This can be done in other ways: database,
168168- // whether temporary or not, cookies, etc.
148148+ id := r.FormValue("nonce_id")
149149+ nonce := r.FormValue("nonce")
150150+151151+ stored, ok := storage.NonceCache().GetAndDelete(id)
152152+ if !ok {
153153+ SendErrorJSON(w, http.StatusBadRequest, "bad_request", "nonce does not match")
154154+ } else if stored.Value() != nonce {
155155+ SendErrorJSON(w, http.StatusBadRequest, "bad_request", "nonce does not match")
156156+ }
157157+169158 req, err := i.Server.ParseAuthorization(r)
170159 if err != nil {
171160 SendErrorJSON(w, http.StatusBadRequest, "invalid_request", err.Error())
172161 return
173162 }
174163175175- // Generate a random code and persist the information associated to that code.
176176- // You could do this in other ways: database, or JWT tokens, or both, for example.
177164 code := i.storeAuthorization(req)
178165179166 // Redirect to client callback.
···183170 http.Redirect(w, r, req.RedirectURI+"?"+query.Encode(), http.StatusFound)
184171}
185172186186-// MustAuth is a middleware to ensure that the request is authorized. The way this
187187-// works depends on the implementation. It then stores the scopes in the context.
188173func MustAuth(next http.Handler) http.Handler {
189174 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
190175 tokenStr := r.Header.Get("Authorization")