package auth import ( "fmt" "net/url" "strings" "time" "backend/app/consts" "backend/app/http/users" "backend/providers/jwt" "backend/providers/otel" "backend/providers/wechat" "github.com/gofiber/fiber/v3" "github.com/pkg/errors" log "github.com/sirupsen/logrus" ) const StatePrefix = "sns_basic_auth" // @provider type Controller struct { svc *Service userSvc *users.Service jwt *jwt.JWT wechat *wechat.Client log *log.Entry `inject:"false"` } func (ctl *Controller) Prepare() error { ctl.log = log.WithField("module", "auth.Controller") return nil } // @Router /v1/auth/wechat/jump/:tenant [get] // @Bind tenant path // @Bind redirectUri query func (ctl *Controller) JumpToAuth(ctx fiber.Ctx, tenant, redirectUri string) error { _, span := otel.Start(ctx.Context(), "auth.controller.wechat") defer span.End() ctl.log.Debugf("%s, query: %v", ctx.OriginalURL(), ctx.Queries()) paramRedirect := ctx.Query("redirect") // 添加 redirect 参数 u, err := url.Parse(string(ctx.Request().URI().FullURI())) if err != nil { return err } query := u.Query() query.Set("redirect", paramRedirect) u.RawQuery = query.Encode() u.Path = "/v1/auth/wechat/login/" + tenant fullUrl := u.String() ctl.log.WithField("module", "middleware.SilentAuth").Debug("redirect_uri: ", fullUrl) to, err := ctl.wechat.ScopeAuthorizeURL( wechat.ScopeAuthorizeURLWithRedirectURI(fullUrl), wechat.ScopeAuthorizeURLWithState(fmt.Sprintf("%s-%d", StatePrefix, time.Now().UnixNano())), ) if err != nil { return errors.Wrap(err, "failed to get wechat auth url") } return ctx.Redirect().To(to.String()) } // @Router /v1/auth/login/:tenant [get] // @Bind tenant path // @Bind code query // @Bind state query // @Bind redirectUri query func (ctl *Controller) Login(ctx fiber.Ctx, code, state, tenant, redirectUri string) error { ctl.log.Debugf("code: %s, state: %s", code, state) ctx.Cookie(&fiber.Cookie{ Name: consts.TokenTypeUser.String(), Value: "", Expires: time.Now().Add(12 * time.Hour), HTTPOnly: true, }) // get the openid token, err := ctl.wechat.AuthorizeCode2Token(code) if err != nil { return errors.Wrap(err, "failed to get openid") } ctl.log.Debugf("tokenInfo %+v", token) user, err := ctl.userSvc.GetOrNewFromChannel(ctx.Context(), consts.AuthChannelWeChat, token.OpenID, tenant) if err != nil { return errors.Wrap(err, "failed to get user") } claim := c.jwt.CreateClaims(jwt.BaseClaims{ OpenID: user.OpenID, Tenant: tenantSlug, UserID: user.ID, TenantID: tenant.ID, }) jwtToken, err = c.jwt.CreateToken(claim) if err != nil { return errors.Wrap(err, "failed to create token") } ctx.Cookie(&fiber.Cookie{ Name: "token", Value: jwtToken, Expires: time.Now().Add(6 * time.Hour), HTTPOnly: true, }) html := strings.ReplaceAll(string(b), "{{JWT}}", jwtToken) return ctx.SendString(html) return ctx.Redirect().To(paramRedirect) }