Files
Olares/cli/pkg/wizard/user_store.go
eball fe3acf669e cli: fix some user activation bugs (#1992)
* fix(cli): update UserBindTerminus to return access token and adjust activation wizard call

* Update wizard.go

feat: ensure authUrl has worked

* Update wizard.go

* feat(cli): add reset password option to user activation command

* feat: add initializeAccount and upload mainvault

* fix: update UserBindTerminus to return access token and improve error handling in RunWizard

* feat: implement AES-GCM encryption in encryptAESGCM function and add necessary imports

* fix: improve account retrieval and error handling in Login and initializeAccount functions

* Update app.go

* feat: update

* fix: comment out TOTP initialization in Signup and adjust account retrieval in Login

---------

Co-authored-by: Peng Peng <billpengpeng@gmail.com>
2025-10-27 18:52:14 +08:00

231 lines
5.9 KiB
Go

package wizard
import (
"fmt"
"log"
"strings"
"time"
"github.com/beclab/Olares/cli/pkg/web5/crypto"
"github.com/beclab/Olares/cli/pkg/web5/dids/did"
"github.com/beclab/Olares/cli/pkg/web5/dids/didcore"
"github.com/beclab/Olares/cli/pkg/web5/jwk"
"github.com/beclab/Olares/cli/pkg/web5/jwt"
)
// Note: DID key-related implementation is now in did_key_utils.go
// UserStore implementation using actual DID keys
type UserStore struct {
terminusName string
mnemonic string
did string
privateJWK *jwk.JWK // Direct use of Web5 JWK structure
mfa string // Store MFA token
}
func (u *UserStore) GetTerminusName() string {
return u.terminusName
}
func (u *UserStore) GetDid() string {
return u.did
}
// SetMFA saves MFA token
func (u *UserStore) SetMFA(mfa string) error {
u.mfa = mfa
log.Printf("MFA token saved to UserStore: %s", mfa)
return nil
}
// GetMFA retrieves MFA token
func (u *UserStore) GetMFA() (string, error) {
if u.mfa == "" {
return "", fmt.Errorf("MFA token not found")
}
return u.mfa, nil
}
func (u *UserStore) GetPrivateJWK() *jwk.JWK {
return u.privateJWK
}
// NewUserStore creates user store, generating all keys from mnemonic (using methods from did_key_utils.go)
func NewUserStore(mnemonic, terminusName string) (*UserStore, error) {
log.Printf("Creating RealUserStore from mnemonic")
// 1. Generate complete DID key result using methods from did_key_utils.go
result, err := GetPrivateJWK(mnemonic)
if err != nil {
return nil, fmt.Errorf("failed to generate DID key: %w", err)
}
log.Printf("Generated DID from mnemonic: %s", result.DID)
// 2. Direct use of Web5's jwk.JWK, no conversion needed
privateJWK := &result.PrivateJWK
return &UserStore{
terminusName: terminusName,
mnemonic: mnemonic,
did: result.DID,
privateJWK: privateJWK,
}, nil
}
// UserStore method implementations
func (u *UserStore) GetCurrentID() string {
return u.did
}
// GetCurrentUser method removed as it was not actually used
func (u *UserStore) GetCurrentUserPrivateKey() (*jwk.JWK, error) {
return u.privateJWK, nil
}
// createBearerDIDFromPrivateKey creates BearerDID from private key
func (u *UserStore) createBearerDIDFromPrivateKey() (did.BearerDID, error) {
// 1. Create LocalKeyManager
keyManager := crypto.NewLocalKeyManager()
// 2. Direct use of our stored Web5 JWK, no conversion needed
privateJWK := *u.privateJWK
// 3. Import private key to KeyManager
keyID, err := keyManager.ImportKey(privateJWK)
if err != nil {
return did.BearerDID{}, fmt.Errorf("failed to import private key: %w", err)
}
// 4. Get public key
publicJWK, err := keyManager.GetPublicKey(keyID)
if err != nil {
return did.BearerDID{}, fmt.Errorf("failed to get public key: %w", err)
}
// 5. Set public key's KID
publicJWK.KID = u.did
publicJWK.USE = "sig"
publicJWK.ALG = "EdDSA"
// 6. Parse DID
parsedDID, err := did.Parse(u.did)
if err != nil {
return did.BearerDID{}, fmt.Errorf("failed to parse DID: %w", err)
}
// 7. Create DID Document
document := didcore.Document{
Context: []string{
"https://www.w3.org/ns/did/v1",
"https://w3id.org/security/suites/ed25519-2020/v1",
},
ID: u.did,
VerificationMethod: []didcore.VerificationMethod{
{
ID: u.did,
Type: "JsonWebKey2020",
Controller: u.did,
PublicKeyJwk: &publicJWK,
},
},
Authentication: []string{"#" + u.did},
AssertionMethod: []string{"#" + u.did},
CapabilityDelegation: []string{"#" + u.did},
CapabilityInvocation: []string{"#" + u.did},
}
fmt.Printf("publicJWK: %v", document)
// 8. Create BearerDID
bearerDID := did.BearerDID{
DID: parsedDID,
KeyManager: keyManager,
Document: document,
}
return bearerDID, nil
}
// SignJWS performs real DID key JWS signing (using BearerDID created from private key)
func (u *UserStore) SignJWS(payload map[string]any) (string, error) {
log.Printf("Creating real JWS signature for DID: %s", u.did)
// Create BearerDID from private key
bearerDID, err := u.createBearerDIDFromPrivateKey()
if err != nil {
return "", fmt.Errorf("failed to create BearerDID from private key: %w", err)
}
// Build JWT Claims (ref: example/main.go)
claims := jwt.Claims{
Issuer: bearerDID.URI,
Misc: payload, // Direct use of passed payload
}
// Ensure payload has necessary fields
if claims.Misc == nil {
claims.Misc = make(map[string]interface{})
}
// Add timestamp if not present
if _, exists := claims.Misc["time"]; !exists {
claims.Misc["time"] = fmt.Sprintf("%d", time.Now().UnixMilli())
}
// Use Web5 JWT signing (ref: example/main.go)
signedJWT, err := jwt.Sign(claims, bearerDID, jwt.Type("JWT"))
if err != nil {
return "", fmt.Errorf("failed to sign JWT: %w", err)
}
log.Printf("Real JWS created successfully with Web5")
log.Printf("Bearer DID: %s", bearerDID.URI)
log.Printf("JWS: %s", signedJWT[:100]+"...")
return signedJWT, nil
}
const TerminusDefaultDomain = "olares.cn"
func (u *UserStore) GetAuthURL() string {
array := strings.Split(u.terminusName, "@")
localURL := u.getLocalURL()
if len(array) == 2 {
return fmt.Sprintf("https://auth.%s%s.%s", localURL, array[0], array[1])
} else {
return fmt.Sprintf("https://auth.%s%s.%s", localURL, array[0], TerminusDefaultDomain)
}
}
func (u *UserStore) GetVaultURL() string {
array := strings.Split(u.terminusName, "@")
localURL := u.getLocalURL()
if len(array) == 2 {
return fmt.Sprintf("https://vault.%s%s.%s/server", localURL, array[0], array[1])
} else {
return fmt.Sprintf("https://vault.%s%s.%s/server", localURL, array[0], TerminusDefaultDomain)
}
}
func (u *UserStore) getLocalURL() string {
return ""
}
func (u *UserStore) GetLocalName() string {
array := strings.Split(u.terminusName, "@")
return array[0]
}
func (u *UserStore) GetDomainName() string {
array := strings.Split(u.terminusName, "@")
if len(array) == 2 {
return array[1]
} else {
return TerminusDefaultDomain
}
}