Compare commits

...

4 Commits

Author SHA1 Message Date
38ee30f455 Can pick up mfa request 2024-06-26 18:18:04 +08:00
ebdb6f5688 🐛 Authenticate wrong payload hotfix 2024-06-26 18:07:07 +08:00
ab5130de2a 🗃️ Add the status model 2024-06-26 17:59:15 +08:00
7aef153bf3 🐛 Fix frontend 2024-06-26 17:22:26 +08:00
17 changed files with 90 additions and 27 deletions

View File

@@ -2,7 +2,7 @@
<project version="4">
<component name="dataSourceStorageLocal" created-in="GO-241.18034.61">
<data-source name="hy_passport@localhost" uuid="74bcf3ef-a2b9-435b-b9e5-f32902a33b25">
<database-info product="PostgreSQL" version="16.2 (Homebrew)" jdbc-version="4.2" driver-name="PostgreSQL JDBC Driver" driver-version="42.6.0" dbms="POSTGRES" exact-version="16.2" exact-driver-version="42.6">
<database-info product="PostgreSQL" version="16.3 (Homebrew)" jdbc-version="4.2" driver-name="PostgreSQL JDBC Driver" driver-version="42.6.0" dbms="POSTGRES" exact-version="16.3" exact-driver-version="42.6">
<identifier-quote-string>&quot;</identifier-quote-string>
</database-info>
<case-sensitivity plain-identifiers="lower" quoted-identifiers="exact" />

View File

@@ -12,7 +12,7 @@
4||10|C|G
4||10|T|G</Grants>
<IntrospectionStateNumber>7430</IntrospectionStateNumber>
<ServerVersion>16.2</ServerVersion>
<ServerVersion>16.3</ServerVersion>
<StartupTime>1716108620</StartupTime>
<TimeZones>true ACDT
true ACSST

23
.idea/workspace.xml generated
View File

@@ -4,11 +4,16 @@
<option name="autoReloadType" value="ALL" />
</component>
<component name="ChangeListManager">
<list default="true" id="3fefb2c4-b6f9-466b-a523-53352e8d6f95" name="更改" comment=":sparkles: Better avatar and banner APIs">
<list default="true" id="3fefb2c4-b6f9-466b-a523-53352e8d6f95" name="更改" comment=":bug: Authenticate wrong payload hotfix">
<change beforePath="$PROJECT_DIR$/.idea/dataSources/74bcf3ef-a2b9-435b-b9e5-f32902a33b25.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/dataSources/74bcf3ef-a2b9-435b-b9e5-f32902a33b25.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/server/api/avatar_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/avatar_api.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/web/src/components/UserMenu.vue" beforeDir="false" afterPath="$PROJECT_DIR$/web/src/components/UserMenu.vue" afterDir="false" />
<change beforePath="$PROJECT_DIR$/web/src/views/dashboard.vue" beforeDir="false" afterPath="$PROJECT_DIR$/web/src/views/dashboard.vue" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/server/api/auth_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/auth_api.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/server/api/index.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/index.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/web/src/layouts/master.vue" beforeDir="false" afterPath="$PROJECT_DIR$/web/src/layouts/master.vue" afterDir="false" />
<change beforePath="$PROJECT_DIR$/web/src/layouts/user-center.vue" beforeDir="false" afterPath="$PROJECT_DIR$/web/src/layouts/user-center.vue" afterDir="false" />
<change beforePath="$PROJECT_DIR$/web/src/router/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/web/src/router/index.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/web/src/views/auth/sign-in.vue" beforeDir="false" afterPath="$PROJECT_DIR$/web/src/views/auth/sign-in.vue" afterDir="false" />
<change beforePath="$PROJECT_DIR$/web/src/views/personalize.vue" beforeDir="false" afterPath="$PROJECT_DIR$/web/src/views/personalize.vue" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -146,10 +151,6 @@
</option>
</component>
<component name="VcsManagerConfiguration">
<MESSAGE value=":bug: Fix APNs non-production" />
<MESSAGE value=":bug: Bug fixes on notification badges for APNs" />
<MESSAGE value=":bug: Fix APNs pushes no sound" />
<MESSAGE value=":bug: Fix APNs pushes no sound (again)" />
<MESSAGE value=":fire: Remove ws connected does not push notify feature" />
<MESSAGE value=":sparkles: Able to read current user's realm profile" />
<MESSAGE value=":sparkles: Consul registration" />
@@ -171,7 +172,11 @@
<MESSAGE value=":arrow_up: Fix notification listen" />
<MESSAGE value=":bug: Fix magic token's foreign key" />
<MESSAGE value=":sparkles: Better avatar and banner APIs" />
<option name="LAST_COMMIT_MESSAGE" value=":sparkles: Better avatar and banner APIs" />
<MESSAGE value=":bug: Fix avatar and banner APIs" />
<MESSAGE value=":bug: Fix frontend" />
<MESSAGE value=":card_file_box: Add the status model" />
<MESSAGE value=":bug: Authenticate wrong payload hotfix" />
<option name="LAST_COMMIT_MESSAGE" value=":bug: Authenticate wrong payload hotfix" />
</component>
<component name="VgoProject">
<settings-migrated>true</settings-migrated>

View File

@@ -11,6 +11,7 @@ var AutoMaintainRange = []any{
&models.AccountProfile{},
&models.AccountContact{},
&models.AccountFriendship{},
&models.Status{},
&models.Badge{},
&models.Realm{},
&models.RealmMember{},

View File

@@ -20,8 +20,10 @@ type Account struct {
ConfirmedAt *time.Time `json:"confirmed_at"`
PermNodes datatypes.JSONMap `json:"perm_nodes"`
Profile AccountProfile `json:"profile"`
Badges []Badge `json:"badges"`
Profile AccountProfile `json:"profile"`
Statuses []Status `json:"statuses"`
Badges []Badge `json:"badges"`
Contacts []AccountContact `json:"contacts"`
RealmIdentities []RealmMember `json:"realm_identities"`

View File

@@ -0,0 +1,20 @@
package models
type StatusAttitude = uint8
const (
AttitudeNeutral = StatusAttitude(iota)
AttitudePositive
AttitudeNegative
)
type Status struct {
BaseModel
Type string `json:"type"`
Label string `json:"label"`
Attitude StatusAttitude `json:"attitude"`
IsNoDisturb bool `json:"is_no_disturb"`
IsInvisible bool `json:"is_invisible"`
AccountID uint `json:"account_id"`
}

View File

@@ -10,9 +10,23 @@ import (
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
)
func getTicket(c *fiber.Ctx) error {
ticketId, err := c.ParamsInt("ticketId")
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "ticket id is required")
}
ticket, err := services.GetTicket(uint(ticketId))
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("ticket %d not found", ticketId))
} else {
return c.JSON(ticket)
}
}
func doAuthenticate(c *fiber.Ctx) error {
var data struct {
Username string `json:"username"`
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
}
@@ -34,7 +48,7 @@ func doAuthenticate(c *fiber.Ctx) error {
ticket, err = services.ActiveTicketWithPassword(ticket, data.Password)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("invalid password: %v", err.Error()))
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("failed to authenticate: %v", err.Error()))
}
return c.JSON(fiber.Map{
@@ -66,7 +80,7 @@ func doMultiFactorAuthenticate(c *fiber.Ctx) error {
ticket, err = services.ActiveTicketWithMFA(ticket, factor, data.Code)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("invalid code: %v", err.Error()))
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("failed to authenticate: %v", err.Error()))
}
return c.JSON(fiber.Map{

View File

@@ -59,6 +59,8 @@ func MapAPIs(app *fiber.App) {
auth.Post("/mfa", doMultiFactorAuthenticate)
auth.Post("/token", getToken)
auth.Get("/tickets/:ticketId", getTicket)
auth.Get("/factors", getAvailableFactors)
auth.Post("/factors/:factorId", requestFactorToken)

View File

@@ -3,6 +3,7 @@ package services
import (
"fmt"
"github.com/samber/lo"
"strings"
"git.solsynth.dev/hydrogen/passport/pkg/internal/database"
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
@@ -99,7 +100,7 @@ func CheckFactor(factor models.AuthFactor, code string) error {
)
case models.EmailPasswordFactor:
return lo.Ternary(
code == factor.Secret,
strings.ToUpper(code) == strings.ToUpper(factor.Secret),
nil,
fmt.Errorf("invalid verification code"),
)

View File

@@ -49,7 +49,7 @@ async function submit() {
const res = await request("/api/auth", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ id: probe.value, password: password.value }),
body: JSON.stringify({ username: probe.value, password: password.value }),
})
if (res.status !== 200) {
error.value = await res.text()

View File

@@ -32,8 +32,7 @@ async function load() {
emits("update:loading", true)
await getToken(props.ticket.grant_token)
await userinfo.readProfiles()
emits("update:loading", false)
setTimeout(() => callback(), 3000)
setTimeout(() => callback(), 1850)
}
onMounted(() => load())

View File

@@ -1,5 +1,5 @@
<template>
<AppBar />
<app-bar />
<v-main>
<router-view />

View File

@@ -1,5 +1,5 @@
<template>
<AppBar>
<app-bar>
<template #extension>
<v-tabs align-tabs="title" color="white">
<v-tab text="Dashboard" prepend-icon="mdi-view-dashboard" :to="{ name: 'dashboard' }" exact />
@@ -7,7 +7,7 @@
<v-tab text="Security" prepend-icon="mdi-security" :to="{ name: 'security' }" exact />
</v-tabs>
</template>
</AppBar>
</app-bar>
<v-main>
<v-container class="pt-6 px-6 p-container">

View File

@@ -39,6 +39,7 @@ const router = createRouter({
children: [
{
path: "/sign-in",
alias: ["/mfa"],
name: "auth.sign-in",
component: () => import("@/views/auth/sign-in.vue"),
meta: { public: true, title: "Sign in" },

View File

@@ -25,7 +25,8 @@
</template>
<script setup lang="ts">
import { type Component, ref } from "vue"
import { type Component, onMounted, ref } from "vue"
import { useRoute } from "vue-router"
import Copyright from "@/components/Copyright.vue"
import CallbackNotify from "@/components/auth/CallbackNotify.vue"
import FactorPicker from "@/components/auth/FactorPicker.vue"
@@ -33,11 +34,28 @@ import FactorApplicator from "@/components/auth/FactorApplicator.vue"
import AccountAuthenticate from "@/components/auth/Authenticate.vue"
import AuthenticateCompleted from "@/components/auth/AuthenticateCompleted.vue"
const route = useRoute()
const loading = ref(false)
const currentFactor = ref<any>(null)
const ticket = ref<any>(null)
async function pickUpTicket() {
if (route.query["ticketId"]) {
loading.value = true
const res = await fetch(`/api/auth/tickets/${route.query["ticketId"]}`)
if (res.status == 200) {
ticket.value = await res.json()
if (ticket.value["available_at"] != null) panel.value = "completed"
else panel.value = "mfa"
}
loading.value = false
}
}
onMounted(() => pickUpTicket())
const panel = ref("authenticate")
const panels: { [id: string]: Component } = {

View File

@@ -1,14 +1,14 @@
<template>
<div>
<v-card>
<v-img cover class="bg-grey-lighten-2" :height="240" src="/api/users/me/avatar" />
<v-img cover class="bg-grey-lighten-2" :height="240" src="/api/users/me/banner" />
<v-card-text class="flex gap-3.5 px-5 pb-5">
<v-avatar
color="grey-lighten-2"
icon="mdi-account-circle"
class="rounded-card"
image="/api/users/me/banner"
image="/api/users/me/avatar"
:size="54"
/>
<div>

View File

@@ -1,6 +1,6 @@
<template>
<div>
<GoUseSolian class="mb-3" />
<go-use-solian class="mb-3" />
<v-card title="Information" prepend-icon="mdi-face-man-profile" :loading="loading">
<template #text>