PSA: change authentication from user/password to token (#13612)
This commit is contained in:
parent
fd653ec838
commit
db8808627a
10 changed files with 380 additions and 97 deletions
10
cmd/token.go
10
cmd/token.go
|
|
@ -51,11 +51,19 @@ func runToken(cmd *cobra.Command, args []string) {
|
||||||
var token *oauth2.Token
|
var token *oauth2.Token
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
switch strings.ToLower(vehicleConf.Type) {
|
typ := strings.ToLower(vehicleConf.Type)
|
||||||
|
if typ == "template" {
|
||||||
|
typ = strings.ToLower(vehicleConf.Other["template"].(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
switch typ {
|
||||||
case "mercedes":
|
case "mercedes":
|
||||||
token, err = mercedesToken()
|
token, err = mercedesToken()
|
||||||
case "tronity":
|
case "tronity":
|
||||||
token, err = tronityToken(conf, vehicleConf)
|
token, err = tronityToken(conf, vehicleConf)
|
||||||
|
case "citroen", "ds", "opel", "peugeot":
|
||||||
|
token, err = psaToken(typ)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
log.FATAL.Fatalf("vehicle type '%s' does not support token authentication", vehicleConf.Type)
|
log.FATAL.Fatalf("vehicle type '%s' does not support token authentication", vehicleConf.Type)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
44
cmd/token_psa.go
Normal file
44
cmd/token_psa.go
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/AlecAivazis/survey/v2"
|
||||||
|
"github.com/evcc-io/evcc/util"
|
||||||
|
"github.com/evcc-io/evcc/util/request"
|
||||||
|
"github.com/evcc-io/evcc/vehicle/psa"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func psaToken(brand string) (*oauth2.Token, error) {
|
||||||
|
var country string
|
||||||
|
prompt_country := &survey.Input{
|
||||||
|
Message: "Please enter your country code:",
|
||||||
|
}
|
||||||
|
if err := survey.AskOne(prompt_country, &country, survey.WithValidator(survey.Required)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cv := oauth2.GenerateVerifier()
|
||||||
|
oc := psa.Oauth2Config(brand, strings.ToLower(country))
|
||||||
|
|
||||||
|
authorize_url := oc.AuthCodeURL("", oauth2.S256ChallengeOption(cv))
|
||||||
|
|
||||||
|
fmt.Println("Please visit: ", authorize_url)
|
||||||
|
fmt.Println("And grab the authorization code like described here: https://github.com/flobz/psa_car_controller/discussions/779")
|
||||||
|
|
||||||
|
var code string
|
||||||
|
prompt_code := &survey.Input{
|
||||||
|
Message: "Please enter your authorization code:",
|
||||||
|
}
|
||||||
|
if err := survey.AskOne(prompt_code, &code, survey.WithValidator(survey.Required)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
client := request.NewClient(util.NewLogger(brand))
|
||||||
|
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, client)
|
||||||
|
|
||||||
|
return oc.Exchange(ctx, code, oauth2.VerifierOption(cv))
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,44 @@
|
||||||
template: citroen
|
template: citroen
|
||||||
products:
|
products:
|
||||||
- brand: Citroën
|
- brand: Citroën
|
||||||
|
requirements:
|
||||||
|
description:
|
||||||
|
de: |
|
||||||
|
Benötigt `access` und `refresh` Tokens. Diese können über den Befehl "evcc token [name]" generiert werden.
|
||||||
|
en: |
|
||||||
|
Citroën `access` and `refresh` tokens are required. These can be generated with command "evcc token [name]".
|
||||||
params:
|
params:
|
||||||
- preset: vehicle-base
|
- name: title
|
||||||
|
- name: icon
|
||||||
|
default: car
|
||||||
|
advanced: true
|
||||||
|
- name: user
|
||||||
|
required: true
|
||||||
|
- name: password
|
||||||
|
deprecated: true
|
||||||
|
- name: tokens
|
||||||
|
required: true
|
||||||
|
mask: true
|
||||||
|
help:
|
||||||
|
en: "See https://docs.evcc.io/en/docs/devices/vehicles#citroen"
|
||||||
|
de: "Siehe https://docs.evcc.io/docs/devices/vehicles#citroen"
|
||||||
|
- name: vin
|
||||||
|
example: V...
|
||||||
|
- name: capacity
|
||||||
|
- name: phases
|
||||||
|
advanced: true
|
||||||
|
- name: password
|
||||||
|
deprecated: true
|
||||||
- preset: vehicle-identify
|
- preset: vehicle-identify
|
||||||
render: |
|
render: |
|
||||||
type: citroen
|
type: citroen
|
||||||
{{ include "vehicle-base" . }}
|
title: {{ .title }}
|
||||||
|
icon: {{ .icon }}
|
||||||
|
user: {{ .user }}
|
||||||
|
tokens:
|
||||||
|
access: {{ .accessToken }}
|
||||||
|
refresh: {{ .refreshToken }}
|
||||||
|
capacity: {{ .capacity }}
|
||||||
|
phases: {{ .phases }}
|
||||||
|
vin: {{ .vin }}
|
||||||
{{ include "vehicle-identify" . }}
|
{{ include "vehicle-identify" . }}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,44 @@
|
||||||
template: ds
|
template: ds
|
||||||
products:
|
products:
|
||||||
- brand: DS
|
- brand: DS
|
||||||
|
requirements:
|
||||||
|
description:
|
||||||
|
de: |
|
||||||
|
Benötigt `access` und `refresh` Tokens. Diese können über den Befehl "evcc token [name]" generiert werden.
|
||||||
|
en: |
|
||||||
|
DS `access` and `refresh` tokens are required. These can be generated with command "evcc token [name]".
|
||||||
params:
|
params:
|
||||||
- preset: vehicle-base
|
- name: title
|
||||||
|
- name: icon
|
||||||
|
default: car
|
||||||
|
advanced: true
|
||||||
|
- name: user
|
||||||
|
required: true
|
||||||
|
- name: password
|
||||||
|
deprecated: true
|
||||||
|
- name: tokens
|
||||||
|
required: true
|
||||||
|
mask: true
|
||||||
|
help:
|
||||||
|
en: "See https://docs.evcc.io/en/docs/devices/vehicles#ds"
|
||||||
|
de: "Siehe https://docs.evcc.io/docs/devices/vehicles#ds"
|
||||||
|
- name: vin
|
||||||
|
example: V...
|
||||||
|
- name: capacity
|
||||||
|
- name: phases
|
||||||
|
advanced: true
|
||||||
|
- name: password
|
||||||
|
deprecated: true
|
||||||
- preset: vehicle-identify
|
- preset: vehicle-identify
|
||||||
render: |
|
render: |
|
||||||
type: ds
|
type: ds
|
||||||
{{ include "vehicle-base" . }}
|
title: {{ .title }}
|
||||||
|
icon: {{ .icon }}
|
||||||
|
user: {{ .user }}
|
||||||
|
tokens:
|
||||||
|
access: {{ .accessToken }}
|
||||||
|
refresh: {{ .refreshToken }}
|
||||||
|
capacity: {{ .capacity }}
|
||||||
|
phases: {{ .phases }}
|
||||||
|
vin: {{ .vin }}
|
||||||
{{ include "vehicle-identify" . }}
|
{{ include "vehicle-identify" . }}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,44 @@
|
||||||
template: opel
|
template: opel
|
||||||
products:
|
products:
|
||||||
- brand: Opel
|
- brand: Opel
|
||||||
|
requirements:
|
||||||
|
description:
|
||||||
|
de: |
|
||||||
|
Benötigt `access` und `refresh` Tokens. Diese können über den Befehl "evcc token [name]" generiert werden.
|
||||||
|
en: |
|
||||||
|
Opel `access` and `refresh` tokens are required. These can be generated with command "evcc token [name]".
|
||||||
params:
|
params:
|
||||||
- preset: vehicle-base
|
- name: title
|
||||||
- preset: vehicle-identify
|
- name: icon
|
||||||
|
default: car
|
||||||
|
advanced: true
|
||||||
|
- name: user
|
||||||
|
required: true
|
||||||
|
- name: password
|
||||||
|
deprecated: true
|
||||||
|
- name: tokens
|
||||||
|
required: true
|
||||||
|
mask: true
|
||||||
|
help:
|
||||||
|
en: "See https://docs.evcc.io/en/docs/devices/vehicles#opel"
|
||||||
|
de: "Siehe https://docs.evcc.io/docs/devices/vehicles#opel"
|
||||||
- name: vin
|
- name: vin
|
||||||
example: WP0...
|
example: V...
|
||||||
|
- name: capacity
|
||||||
|
- name: phases
|
||||||
|
advanced: true
|
||||||
|
- name: password
|
||||||
|
deprecated: true
|
||||||
|
- preset: vehicle-identify
|
||||||
render: |
|
render: |
|
||||||
type: opel
|
type: opel
|
||||||
{{ include "vehicle-base" . }}
|
title: {{ .title }}
|
||||||
|
icon: {{ .icon }}
|
||||||
|
user: {{ .user }}
|
||||||
|
tokens:
|
||||||
|
access: {{ .accessToken }}
|
||||||
|
refresh: {{ .refreshToken }}
|
||||||
|
capacity: {{ .capacity }}
|
||||||
|
phases: {{ .phases }}
|
||||||
|
vin: {{ .vin }}
|
||||||
{{ include "vehicle-identify" . }}
|
{{ include "vehicle-identify" . }}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,44 @@
|
||||||
template: peugeot
|
template: peugeot
|
||||||
products:
|
products:
|
||||||
- brand: Peugeot
|
- brand: Peugeot
|
||||||
|
requirements:
|
||||||
|
description:
|
||||||
|
de: |
|
||||||
|
Benötigt `access` und `refresh` Tokens. Diese können über den Befehl "evcc token [name]" generiert werden.
|
||||||
|
en: |
|
||||||
|
Peugeot `access` and `refresh` tokens are required. These can be generated with command "evcc token [name]".
|
||||||
params:
|
params:
|
||||||
- preset: vehicle-base
|
- name: title
|
||||||
|
- name: icon
|
||||||
|
default: car
|
||||||
|
advanced: true
|
||||||
|
- name: user
|
||||||
|
required: true
|
||||||
|
- name: password
|
||||||
|
deprecated: true
|
||||||
|
- name: tokens
|
||||||
|
required: true
|
||||||
|
mask: true
|
||||||
|
help:
|
||||||
|
en: "See https://docs.evcc.io/en/docs/devices/vehicles#peugeot"
|
||||||
|
de: "Siehe https://docs.evcc.io/docs/devices/vehicles#peugeot"
|
||||||
|
- name: vin
|
||||||
|
example: V...
|
||||||
|
- name: capacity
|
||||||
|
- name: phases
|
||||||
|
advanced: true
|
||||||
|
- name: password
|
||||||
|
deprecated: true
|
||||||
- preset: vehicle-identify
|
- preset: vehicle-identify
|
||||||
render: |
|
render: |
|
||||||
type: peugeot
|
type: peugeot
|
||||||
{{ include "vehicle-base" . }}
|
title: {{ .title }}
|
||||||
|
icon: {{ .icon }}
|
||||||
|
user: {{ .user }}
|
||||||
|
tokens:
|
||||||
|
access: {{ .accessToken }}
|
||||||
|
refresh: {{ .refreshToken }}
|
||||||
|
capacity: {{ .capacity }}
|
||||||
|
phases: {{ .phases }}
|
||||||
|
vin: {{ .vin }}
|
||||||
{{ include "vehicle-identify" . }}
|
{{ include "vehicle-identify" . }}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package vehicle
|
package vehicle
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/evcc-io/evcc/api"
|
"github.com/evcc-io/evcc/api"
|
||||||
|
|
@ -12,46 +12,18 @@ import (
|
||||||
// https://github.com/TA2k/ioBroker.psa
|
// https://github.com/TA2k/ioBroker.psa
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
registry.Add("citroen", NewCitroenFromConfig)
|
registry.Add("citroen", func(other map[string]any) (api.Vehicle, error) {
|
||||||
registry.Add("ds", NewDSFromConfig)
|
return newPSA("citroen", "clientsB2CCitroen", other)
|
||||||
registry.Add("opel", NewOpelFromConfig)
|
})
|
||||||
registry.Add("peugeot", NewPeugeotFromConfig)
|
registry.Add("ds", func(other map[string]any) (api.Vehicle, error) {
|
||||||
}
|
return newPSA("ds", "clientsB2CDS", other)
|
||||||
|
})
|
||||||
// NewCitroenFromConfig creates a new vehicle
|
registry.Add("opel", func(other map[string]any) (api.Vehicle, error) {
|
||||||
func NewCitroenFromConfig(other map[string]interface{}) (api.Vehicle, error) {
|
return newPSA("opel", "clientsB2COpel", other)
|
||||||
log := util.NewLogger("citroen")
|
})
|
||||||
return newPSA(log,
|
registry.Add("peugeot", func(other map[string]any) (api.Vehicle, error) {
|
||||||
"citroen.com", "clientsB2CCitroen",
|
return newPSA("peugeot", "clientsB2CPeugeot", other)
|
||||||
"5364defc-80e6-447b-bec6-4af8d1542cae", "iE0cD8bB0yJ0dS6rO3nN1hI2wU7uA5xR4gP7lD6vM0oH0nS8dN",
|
})
|
||||||
other)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDSFromConfig creates a new vehicle
|
|
||||||
func NewDSFromConfig(other map[string]interface{}) (api.Vehicle, error) {
|
|
||||||
log := util.NewLogger("ds")
|
|
||||||
return newPSA(log,
|
|
||||||
"driveds.com", "clientsB2CDS",
|
|
||||||
"cbf74ee7-a303-4c3d-aba3-29f5994e2dfa", "X6bE6yQ3tH1cG5oA6aW4fS6hK0cR0aK5yN2wE4hP8vL8oW5gU3",
|
|
||||||
other)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewOpelFromConfig creates a new vehicle
|
|
||||||
func NewOpelFromConfig(other map[string]interface{}) (api.Vehicle, error) {
|
|
||||||
log := util.NewLogger("opel")
|
|
||||||
return newPSA(log,
|
|
||||||
"opel.com", "clientsB2COpel",
|
|
||||||
"07364655-93cb-4194-8158-6b035ac2c24c", "F2kK7lC5kF5qN7tM0wT8kE3cW1dP0wC5pI6vC0sQ5iP5cN8cJ8",
|
|
||||||
other)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPeugeotFromConfig creates a new vehicle
|
|
||||||
func NewPeugeotFromConfig(other map[string]interface{}) (api.Vehicle, error) {
|
|
||||||
log := util.NewLogger("peugeot")
|
|
||||||
return newPSA(log,
|
|
||||||
"peugeot.com", "clientsB2CPeugeot",
|
|
||||||
"1eebc2d5-5df3-459b-a624-20abfcf82530", "T5tP7iS0cO8sC0lA2iE2aR7gK6uE5rF3lJ8pC3nO1pR7tL8vU1",
|
|
||||||
other)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PSA is an api.Vehicle implementation for PSA cars
|
// PSA is an api.Vehicle implementation for PSA cars
|
||||||
|
|
@ -61,17 +33,16 @@ type PSA struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// newPSA creates a new vehicle
|
// newPSA creates a new vehicle
|
||||||
func newPSA(log *util.Logger, brand, realm, id, secret string, other map[string]interface{}) (api.Vehicle, error) {
|
func newPSA(brand, realm string, other map[string]interface{}) (api.Vehicle, error) {
|
||||||
cc := struct {
|
cc := struct {
|
||||||
embed `mapstructure:",squash"`
|
embed `mapstructure:",squash"`
|
||||||
Credentials ClientCredentials
|
VIN string
|
||||||
User, Password, VIN string
|
User string
|
||||||
Cache time.Duration
|
Password string `mapstructure:"password"`
|
||||||
|
Country string
|
||||||
|
Tokens Tokens
|
||||||
|
Cache time.Duration
|
||||||
}{
|
}{
|
||||||
Credentials: ClientCredentials{
|
|
||||||
ID: id,
|
|
||||||
Secret: secret,
|
|
||||||
},
|
|
||||||
Cache: interval,
|
Cache: interval,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,22 +50,30 @@ func newPSA(log *util.Logger, brand, realm, id, secret string, other map[string]
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if cc.User == "" || cc.Password == "" {
|
if cc.User == "" {
|
||||||
return nil, api.ErrMissingCredentials
|
return nil, api.ErrMissingCredentials
|
||||||
}
|
}
|
||||||
|
|
||||||
|
token, err := cc.Tokens.Token()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
v := &PSA{
|
v := &PSA{
|
||||||
embed: &cc.embed,
|
embed: &cc.embed,
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Redact(cc.User, cc.Password, cc.VIN)
|
log := util.NewLogger(brand)
|
||||||
identity := psa.NewIdentity(log, brand, cc.Credentials.ID, cc.Credentials.Secret)
|
log.Redact(cc.User, cc.Tokens.Access, cc.Tokens.Refresh)
|
||||||
|
|
||||||
if err := identity.Login(cc.User, cc.Password); err != nil {
|
oc := psa.Oauth2Config(brand, strings.ToLower(cc.Country))
|
||||||
return v, fmt.Errorf("login failed: %w", err)
|
identity, err := psa.NewIdentity(log, brand, cc.User, oc, token)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
api := psa.NewAPI(log, identity, realm, cc.Credentials.ID)
|
// TODO still needed?
|
||||||
|
api := psa.NewAPI(log, identity, realm, oc.ClientID)
|
||||||
|
|
||||||
vehicle, err := ensureVehicleEx(
|
vehicle, err := ensureVehicleEx(
|
||||||
cc.VIN, api.Vehicles,
|
cc.VIN, api.Vehicles,
|
||||||
|
|
|
||||||
20
vehicle/psa/helper.go
Normal file
20
vehicle/psa/helper.go
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
package psa
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
mu sync.Mutex
|
||||||
|
identities = make(map[string]oauth2.TokenSource)
|
||||||
|
)
|
||||||
|
|
||||||
|
func getInstance(subject string) oauth2.TokenSource {
|
||||||
|
return identities[subject]
|
||||||
|
}
|
||||||
|
|
||||||
|
func addInstance(subject string, identity oauth2.TokenSource) {
|
||||||
|
identities[subject] = identity
|
||||||
|
}
|
||||||
|
|
@ -2,48 +2,80 @@ package psa
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"errors"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/evcc-io/evcc/server/db/settings"
|
||||||
"github.com/evcc-io/evcc/util"
|
"github.com/evcc-io/evcc/util"
|
||||||
|
"github.com/evcc-io/evcc/util/oauth"
|
||||||
"github.com/evcc-io/evcc/util/request"
|
"github.com/evcc-io/evcc/util/request"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Identity struct {
|
type Identity struct {
|
||||||
*request.Helper
|
|
||||||
oc *oauth2.Config
|
|
||||||
oauth2.TokenSource
|
oauth2.TokenSource
|
||||||
|
mu sync.Mutex
|
||||||
|
oc *oauth2.Config
|
||||||
|
log *util.Logger
|
||||||
|
subject string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIdentity creates PSA identity
|
// NewIdentity creates PSA identity
|
||||||
func NewIdentity(log *util.Logger, brand, id, secret string) *Identity {
|
func NewIdentity(log *util.Logger, brand, user string, oc *oauth2.Config, token *oauth2.Token) (oauth2.TokenSource, error) {
|
||||||
return &Identity{
|
// serialise instance handling
|
||||||
Helper: request.NewHelper(log),
|
mu.Lock()
|
||||||
oc: &oauth2.Config{
|
defer mu.Unlock()
|
||||||
ClientID: id,
|
|
||||||
ClientSecret: secret,
|
|
||||||
Endpoint: oauth2.Endpoint{
|
|
||||||
AuthURL: "https://api.mpsa.com/api/connectedcar/v2/oauth/authorize",
|
|
||||||
TokenURL: fmt.Sprintf("https://idpcvs.%s/am/oauth2/access_token", brand),
|
|
||||||
AuthStyle: oauth2.AuthStyleInHeader,
|
|
||||||
},
|
|
||||||
Scopes: []string{"openid profile"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Identity) Login(user, password string) error {
|
// reuse identity instance
|
||||||
ctx := context.WithValue(
|
subject := "psa." + strings.ToLower(brand) + "." + strings.ToLower(user)
|
||||||
context.Background(),
|
if instance := getInstance(subject); instance != nil {
|
||||||
oauth2.HTTPClient,
|
return instance, nil
|
||||||
v.Client,
|
|
||||||
)
|
|
||||||
|
|
||||||
// replace client with authenticated oauth client
|
|
||||||
token, err := v.oc.PasswordCredentialsToken(ctx, user, password)
|
|
||||||
if err == nil {
|
|
||||||
v.TokenSource = v.oc.TokenSource(ctx, token)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
v := &Identity{
|
||||||
|
log: log,
|
||||||
|
oc: oc,
|
||||||
|
subject: subject,
|
||||||
|
}
|
||||||
|
|
||||||
|
var tok oauth2.Token
|
||||||
|
if err := settings.Json(v.subject, &tok); err == nil {
|
||||||
|
token = &tok
|
||||||
|
}
|
||||||
|
|
||||||
|
if !token.Valid() {
|
||||||
|
if tok, err := v.RefreshToken(token); err == nil {
|
||||||
|
token = tok
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !token.Valid() {
|
||||||
|
return nil, errors.New("token expired")
|
||||||
|
}
|
||||||
|
|
||||||
|
v.TokenSource = oauth.RefreshTokenSource(token, v)
|
||||||
|
|
||||||
|
// add instance
|
||||||
|
addInstance(v.subject, v)
|
||||||
|
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Identity) RefreshToken(token *oauth2.Token) (*oauth2.Token, error) {
|
||||||
|
v.mu.Lock()
|
||||||
|
defer v.mu.Unlock()
|
||||||
|
|
||||||
|
client := request.NewClient(v.log)
|
||||||
|
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, client)
|
||||||
|
|
||||||
|
tok, err := v.oc.TokenSource(ctx, token).Token()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
v.TokenSource = oauth.RefreshTokenSource(tok, v)
|
||||||
|
err = settings.SetJson(v.subject, tok)
|
||||||
|
|
||||||
|
return tok, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
66
vehicle/psa/oauth2.go
Normal file
66
vehicle/psa/oauth2.go
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
package psa
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Oauth2Config(brand, country string) *oauth2.Config {
|
||||||
|
switch brand {
|
||||||
|
case "citroen":
|
||||||
|
return &oauth2.Config{
|
||||||
|
ClientID: "5364defc-80e6-447b-bec6-4af8d1542cae",
|
||||||
|
ClientSecret: "iE0cD8bB0yJ0dS6rO3nN1hI2wU7uA5xR4gP7lD6vM0oH0nS8dN",
|
||||||
|
Endpoint: oauth2.Endpoint{
|
||||||
|
AuthURL: "https://idpcvs.citroen.com/am/oauth2/authorize",
|
||||||
|
TokenURL: "https://idpcvs.citroen.com/am/oauth2/access_token",
|
||||||
|
AuthStyle: oauth2.AuthStyleInHeader,
|
||||||
|
},
|
||||||
|
RedirectURL: fmt.Sprintf("mymacsdk://oauth2redirect/%s", country),
|
||||||
|
Scopes: []string{"openid", "profile"},
|
||||||
|
}
|
||||||
|
|
||||||
|
case "ds":
|
||||||
|
return &oauth2.Config{
|
||||||
|
ClientID: "cbf74ee7-a303-4c3d-aba3-29f5994e2dfa",
|
||||||
|
ClientSecret: "X6bE6yQ3tH1cG5oA6aW4fS6hK0cR0aK5yN2wE4hP8vL8oW5gU3",
|
||||||
|
Endpoint: oauth2.Endpoint{
|
||||||
|
AuthURL: "https://idpcvs.driveds.com/am/oauth2/authorize",
|
||||||
|
TokenURL: "https://idpcvs.driveds.com/am/oauth2/access_token",
|
||||||
|
AuthStyle: oauth2.AuthStyleInHeader,
|
||||||
|
},
|
||||||
|
RedirectURL: fmt.Sprintf("mymdssdk://oauth2redirect/%s", country),
|
||||||
|
Scopes: []string{"openid", "profile"},
|
||||||
|
}
|
||||||
|
|
||||||
|
case "opel":
|
||||||
|
return &oauth2.Config{
|
||||||
|
ClientID: "07364655-93cb-4194-8158-6b035ac2c24c",
|
||||||
|
ClientSecret: "F2kK7lC5kF5qN7tM0wT8kE3cW1dP0wC5pI6vC0sQ5iP5cN8cJ8",
|
||||||
|
Endpoint: oauth2.Endpoint{
|
||||||
|
AuthURL: "https://idpcvs.opel.com/am/oauth2/authorize",
|
||||||
|
TokenURL: "https://idpcvs.opel.com/am/oauth2/access_token",
|
||||||
|
AuthStyle: oauth2.AuthStyleInHeader,
|
||||||
|
},
|
||||||
|
RedirectURL: fmt.Sprintf("mymopsdk://oauth2redirect/%s", country),
|
||||||
|
Scopes: []string{"openid", "profile"},
|
||||||
|
}
|
||||||
|
|
||||||
|
case "peugeot":
|
||||||
|
return &oauth2.Config{
|
||||||
|
ClientID: "1eebc2d5-5df3-459b-a624-20abfcf82530",
|
||||||
|
ClientSecret: "T5tP7iS0cO8sC0lA2iE2aR7gK6uE5rF3lJ8pC3nO1pR7tL8vU1",
|
||||||
|
Endpoint: oauth2.Endpoint{
|
||||||
|
AuthURL: "https://idpcvs.peugeot.com/am/oauth2/authorize",
|
||||||
|
TokenURL: "https://idpcvs.peugeot.com/am/oauth2/access_token",
|
||||||
|
AuthStyle: oauth2.AuthStyleInHeader,
|
||||||
|
},
|
||||||
|
RedirectURL: fmt.Sprintf("mymap://oauth2redirect/%s", country),
|
||||||
|
Scopes: []string{"openid", "profile"},
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic("invalid brand: " + brand)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue