2022-02-17 16:16:15 +00:00
|
|
|
package http
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/fasthttp/router"
|
2022-06-09 19:14:21 +00:00
|
|
|
"github.com/lestrrat-go/jwx/v2/jwa"
|
2022-02-17 16:16:15 +00:00
|
|
|
http "github.com/valyala/fasthttp"
|
|
|
|
|
2022-03-13 10:58:34 +00:00
|
|
|
"source.toby3d.me/toby3d/auth/internal/common"
|
|
|
|
"source.toby3d.me/toby3d/auth/internal/domain"
|
|
|
|
"source.toby3d.me/toby3d/auth/internal/token"
|
2022-03-25 18:07:52 +00:00
|
|
|
"source.toby3d.me/toby3d/middleware"
|
2022-02-17 16:16:15 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type (
|
|
|
|
UserInformationResponse struct {
|
2022-02-17 19:22:20 +00:00
|
|
|
Name string `json:"name,omitempty"`
|
|
|
|
URL string `json:"url,omitempty"`
|
|
|
|
Photo string `json:"photo,omitempty"`
|
|
|
|
Email string `json:"email,omitempty"`
|
2022-02-17 16:16:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
RequestHandler struct {
|
|
|
|
config *domain.Config
|
|
|
|
tokens token.UseCase
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
func NewRequestHandler(tokens token.UseCase, config *domain.Config) *RequestHandler {
|
|
|
|
return &RequestHandler{
|
|
|
|
tokens: tokens,
|
|
|
|
config: config,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *RequestHandler) Register(r *router.Router) {
|
|
|
|
chain := middleware.Chain{
|
2022-03-25 18:07:52 +00:00
|
|
|
//nolint: exhaustivestruct
|
2022-02-17 16:16:15 +00:00
|
|
|
middleware.JWTWithConfig(middleware.JWTConfig{
|
2022-02-25 23:18:04 +00:00
|
|
|
AuthScheme: "Bearer",
|
|
|
|
ContextKey: "token",
|
|
|
|
SigningKey: []byte(h.config.JWT.Secret),
|
|
|
|
SigningMethod: jwa.SignatureAlgorithm(h.config.JWT.Algorithm),
|
|
|
|
Skipper: middleware.DefaultSkipper,
|
|
|
|
TokenLookup: "header:" + http.HeaderAuthorization + ":Bearer ",
|
2022-02-17 16:16:15 +00:00
|
|
|
}),
|
|
|
|
middleware.LogFmt(),
|
|
|
|
}
|
|
|
|
|
|
|
|
r.GET("/userinfo", chain.RequestHandler(h.handleUserInformation))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *RequestHandler) handleUserInformation(ctx *http.RequestCtx) {
|
|
|
|
ctx.SetContentType(common.MIMEApplicationJSONCharsetUTF8)
|
|
|
|
ctx.SetStatusCode(http.StatusOK)
|
|
|
|
|
|
|
|
encoder := json.NewEncoder(ctx)
|
|
|
|
|
2022-02-25 15:34:20 +00:00
|
|
|
tkn, userInfo, err := h.tokens.Verify(ctx, strings.TrimPrefix(string(ctx.Request.Header.Peek(
|
|
|
|
http.HeaderAuthorization)), "Bearer "))
|
2022-02-17 16:16:15 +00:00
|
|
|
if err != nil || tkn == nil {
|
|
|
|
// WARN(toby3d): If the token is not valid, the endpoint still
|
|
|
|
// MUST return a 200 Response.
|
2022-03-25 18:07:52 +00:00
|
|
|
_ = encoder.Encode(err) //nolint: errchkjson
|
2022-02-17 16:16:15 +00:00
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-02-17 19:22:20 +00:00
|
|
|
if !tkn.Scope.Has(domain.ScopeProfile) {
|
2022-02-17 16:16:15 +00:00
|
|
|
ctx.SetStatusCode(http.StatusForbidden)
|
2022-02-17 19:22:20 +00:00
|
|
|
|
2022-03-25 18:07:52 +00:00
|
|
|
//nolint: errchkjson
|
2022-02-17 16:16:15 +00:00
|
|
|
_ = encoder.Encode(domain.NewError(
|
|
|
|
domain.ErrorCodeInsufficientScope,
|
2022-02-17 19:22:20 +00:00
|
|
|
"token with 'profile' scope is required to view profile data",
|
2022-02-17 16:16:15 +00:00
|
|
|
"https://indieauth.net/source/#user-information",
|
|
|
|
))
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-02-17 19:22:20 +00:00
|
|
|
resp := new(UserInformationResponse)
|
2022-02-25 15:34:20 +00:00
|
|
|
if userInfo == nil {
|
2022-03-25 18:07:52 +00:00
|
|
|
_ = encoder.Encode(resp) //nolint: errchkjson
|
2022-02-17 19:22:20 +00:00
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-02-25 15:34:20 +00:00
|
|
|
if userInfo.HasName() {
|
|
|
|
resp.Name = userInfo.GetName()
|
|
|
|
}
|
2022-02-17 19:22:20 +00:00
|
|
|
|
2022-02-25 15:34:20 +00:00
|
|
|
if userInfo.HasURL() {
|
|
|
|
resp.URL = userInfo.GetURL().String()
|
2022-02-17 21:47:13 +00:00
|
|
|
}
|
|
|
|
|
2022-02-25 15:34:20 +00:00
|
|
|
if userInfo.HasPhoto() {
|
|
|
|
resp.Photo = userInfo.GetPhoto().String()
|
2022-02-17 21:47:13 +00:00
|
|
|
}
|
|
|
|
|
2022-02-25 15:34:20 +00:00
|
|
|
if tkn.Scope.Has(domain.ScopeEmail) && userInfo.HasEmail() {
|
|
|
|
resp.Email = userInfo.GetEmail().String()
|
2022-02-17 19:22:20 +00:00
|
|
|
}
|
|
|
|
|
2022-03-25 18:07:52 +00:00
|
|
|
_ = encoder.Encode(resp) //nolint: errchkjson
|
2022-02-17 16:16:15 +00:00
|
|
|
}
|