Auth UI: auto-prepare, success message, extend tests (#26077)
This commit is contained in:
parent
86aeb8e542
commit
4d09af22f7
14 changed files with 376 additions and 89 deletions
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/evcc-io/evcc/api"
|
||||
|
|
@ -17,43 +18,53 @@ func init() {
|
|||
}
|
||||
|
||||
type demo struct {
|
||||
token *oauth2.Token
|
||||
region string
|
||||
method string
|
||||
token *oauth2.Token
|
||||
server string
|
||||
method string
|
||||
redirectUri string
|
||||
onlineC chan<- bool
|
||||
}
|
||||
|
||||
var demoInstance *demo
|
||||
|
||||
func NewDemoFromConfig(_ context.Context, other map[string]any) (oauth2.TokenSource, error) {
|
||||
var cc struct {
|
||||
Region string
|
||||
Method string
|
||||
Server string
|
||||
Method string
|
||||
RedirectUri string
|
||||
}
|
||||
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewDemo(cc.Region, cc.Method)
|
||||
return NewDemo(cc.Server, cc.Method, cc.RedirectUri)
|
||||
}
|
||||
|
||||
func NewDemo(region string, method string) (oauth2.TokenSource, error) {
|
||||
func NewDemo(server string, method string, redirectUri string) (oauth2.TokenSource, error) {
|
||||
// reuse instance (similar to oauth.go getInstance pattern)
|
||||
if demoInstance != nil {
|
||||
// update existing instance with new values
|
||||
demoInstance.region = region
|
||||
demoInstance.server = server
|
||||
demoInstance.method = method
|
||||
demoInstance.redirectUri = redirectUri
|
||||
return demoInstance, nil
|
||||
}
|
||||
|
||||
demoInstance = &demo{
|
||||
region: region,
|
||||
method: method,
|
||||
server: server,
|
||||
method: method,
|
||||
redirectUri: redirectUri,
|
||||
}
|
||||
|
||||
if _, err := providerauth.Register("demo", demoInstance); err != nil {
|
||||
onlineC, err := providerauth.Register("demo", demoInstance)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
demoInstance.onlineC = onlineC
|
||||
|
||||
// Send initial auth status
|
||||
demoInstance.onlineC <- false
|
||||
|
||||
return demoInstance, nil
|
||||
}
|
||||
|
|
@ -65,41 +76,63 @@ func (o *demo) Token() (*oauth2.Token, error) {
|
|||
return o.token, nil
|
||||
}
|
||||
|
||||
func (o *demo) Login(_ string) (string, *oauth2.DeviceAuthResponse, error) {
|
||||
// Simulate error for ERROR region
|
||||
if o.region == "ERROR" {
|
||||
return "", nil, fmt.Errorf("region not supported")
|
||||
func (o *demo) Login(state string) (string, *oauth2.DeviceAuthResponse, error) {
|
||||
// Validate server URL has proper scheme
|
||||
if !strings.HasPrefix(o.server, "http://") && !strings.HasPrefix(o.server, "https://") {
|
||||
return "", nil, fmt.Errorf("server must start with http:// or https://")
|
||||
}
|
||||
|
||||
// For demo, immediately authenticate without requiring external flow
|
||||
o.token = &oauth2.Token{
|
||||
AccessToken: "demo-token",
|
||||
Expiry: time.Now().Add(24 * time.Hour),
|
||||
// Validate redirect URI has proper scheme
|
||||
if !strings.HasPrefix(o.redirectUri, "http://") && !strings.HasPrefix(o.redirectUri, "https://") {
|
||||
return "", nil, fmt.Errorf("redirectUri must start with http:// or https://")
|
||||
}
|
||||
|
||||
// TODO use network settings after https://github.com/evcc-io/evcc/pull/25141
|
||||
uri := "http://localhost:7070/providerauth/callback"
|
||||
// Build mock login URL with state and redirectUri (complete callback URL)
|
||||
values := url.Values{}
|
||||
values.Set("state", state)
|
||||
values.Set("redirectUri", o.redirectUri)
|
||||
|
||||
mockLoginURL := fmt.Sprintf("%s/mock-login?%s", o.server, values.Encode())
|
||||
|
||||
if o.method == "device-code" {
|
||||
// Device code flow: URI comes from DeviceAuthResponse
|
||||
return "", &oauth2.DeviceAuthResponse{
|
||||
UserCode: "12AB345",
|
||||
VerificationURI: uri,
|
||||
VerificationURI: mockLoginURL,
|
||||
Expiry: time.Now().Add(10 * time.Minute),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Redirect flow: URI in first return value
|
||||
return uri, nil, nil
|
||||
return mockLoginURL, nil, nil
|
||||
}
|
||||
|
||||
func (o *demo) Logout() error {
|
||||
o.token = nil
|
||||
if o.onlineC != nil {
|
||||
o.onlineC <- false
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *demo) HandleCallback(params url.Values) error {
|
||||
// no-op: token already set in Login()
|
||||
// Extract code from callback parameters
|
||||
code := params.Get("code")
|
||||
if code == "" {
|
||||
return fmt.Errorf("missing code parameter")
|
||||
}
|
||||
|
||||
// Create token based on code (for demo, we use a fixed token)
|
||||
o.token = &oauth2.Token{
|
||||
AccessToken: code, // Use the code as the access token
|
||||
Expiry: time.Now().Add(24 * time.Hour),
|
||||
}
|
||||
|
||||
// Notify that authentication succeeded
|
||||
if o.onlineC != nil {
|
||||
o.onlineC <- true
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -108,5 +141,5 @@ func (o *demo) Authenticated() bool {
|
|||
}
|
||||
|
||||
func (o *demo) DisplayName() string {
|
||||
return "demo"
|
||||
return "Demo Auth"
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue