feat: add token IDs, store tokens in db for early invalidation

This commit is contained in:
Sam 2023-01-01 00:34:38 +01:00
parent 58c1c1794e
commit e5723360a7
Signed by: sam
GPG key ID: B4EF20DDE721CAA1
7 changed files with 248 additions and 9 deletions

View file

@ -11,6 +11,7 @@ import (
"github.com/bwmarrin/discordgo"
"github.com/go-chi/render"
"github.com/mediocregopher/radix/v4"
"github.com/rs/xid"
"golang.org/x/oauth2"
)
@ -81,11 +82,19 @@ func (s *Server) discordCallback(w http.ResponseWriter, r *http.Request) error {
log.Errorf("updating user %v with Discord info: %v", u.ID, err)
}
token, err := s.Auth.CreateToken(u.ID)
// TODO: implement user + token permissions
tokenID := xid.New()
token, err := s.Auth.CreateToken(u.ID, tokenID, false, true)
if err != nil {
return err
}
// save token to database
_, err = s.DB.SaveToken(ctx, u.ID, tokenID)
if err != nil {
return errors.Wrap(err, "saving token to database")
}
render.JSON(w, r, discordCallbackResponse{
HasAccount: true,
Token: token,
@ -206,11 +215,19 @@ func (s *Server) discordSignup(w http.ResponseWriter, r *http.Request) error {
}
// create token
token, err := s.Auth.CreateToken(u.ID)
// TODO: implement user + token permissions
tokenID := xid.New()
token, err := s.Auth.CreateToken(u.ID, tokenID, false, true)
if err != nil {
return errors.Wrap(err, "creating token")
}
// save token to database
_, err = s.DB.SaveToken(ctx, u.ID, tokenID)
if err != nil {
return errors.Wrap(err, "saving token to database")
}
// return user
render.JSON(w, r, signupResponse{
User: *dbUserToUserResponse(u),

View file

@ -67,6 +67,11 @@ func Mount(srv *server.Server, r chi.Router) {
// invite routes
r.With(server.MustAuth).Get("/invites", server.WrapHandler(s.getInvites))
r.With(server.MustAuth).Post("/invites", server.WrapHandler(s.createInvite))
// tokens
r.With(server.MustAuth).Get("/tokens", server.WrapHandler(s.getTokens))
r.With(server.MustAuth).Post("/tokens", server.WrapHandler(s.createToken))
r.With(server.MustAuth).Delete("/tokens", server.WrapHandler(s.deleteToken))
})
}

View file

@ -0,0 +1,72 @@
package auth
import (
"net/http"
"time"
"codeberg.org/u1f320/pronouns.cc/backend/db"
"codeberg.org/u1f320/pronouns.cc/backend/server"
"emperror.dev/errors"
"github.com/go-chi/render"
"github.com/rs/xid"
)
type getTokenResponse struct {
TokenID xid.ID `json:"id"`
Created time.Time `json:"created"`
Expires time.Time `json:"expires"`
}
func dbTokenToGetResponse(t db.Token) getTokenResponse {
return getTokenResponse{
TokenID: t.TokenID,
Created: t.Created,
Expires: t.Expires,
}
}
func (s *Server) getTokens(w http.ResponseWriter, r *http.Request) error {
ctx := r.Context()
claims, _ := server.ClaimsFromContext(ctx)
tokens, err := s.DB.Tokens(ctx, claims.UserID)
if err != nil {
return errors.Wrap(err, "getting tokens")
}
resps := make([]getTokenResponse, len(tokens))
for i := range tokens {
resps[i] = dbTokenToGetResponse(tokens[i])
}
render.JSON(w, r, resps)
return nil
}
type deleteTokenResponse struct {
TokenID xid.ID `json:"id"`
Invalidated bool `json:"invalidated"`
Created time.Time `json:"time"`
}
func (s *Server) deleteToken(w http.ResponseWriter, r *http.Request) error {
ctx := r.Context()
claims, _ := server.ClaimsFromContext(ctx)
t, err := s.DB.InvalidateToken(ctx, claims.UserID, claims.TokenID)
if err != nil {
return errors.Wrap(err, "invalidating token")
}
render.JSON(w, r, deleteTokenResponse{
TokenID: t.TokenID,
Invalidated: t.Invalidated,
Created: t.Created,
})
return nil
}
func (s *Server) createToken(w http.ResponseWriter, r *http.Request) error {
// unimplemented right now
return server.APIError{Code: server.ErrForbidden}
}