From 11e769ea22cdf168623b7ed121cb01233cd672e0 Mon Sep 17 00:00:00 2001 From: lehmanju Date: Tue, 5 Aug 2025 23:16:49 +0200 Subject: [PATCH] Improve oauth integration (#21266) --- .storybook/preview.js | 6 ++ api/api.go | 10 +- assets/js/components/Site/Site.vue | 8 +- assets/js/components/Top/Header.vue | 5 +- .../js/components/Top/Navigation.stories.ts | 22 ++--- assets/js/components/Top/Navigation.vue | 70 +++++++------- assets/js/components/Top/types.d.ts | 2 +- assets/js/types/evcc.ts | 8 +- cmd/root.go | 2 +- cmd/setup.go | 8 +- core/keys/global.go | 1 + i18n/de.json | 5 +- i18n/en.json | 5 +- plugin/auth/oauth.go | 81 ++++++++++------- .../providerauth.go} | 91 +++++++++++++++---- .../definition/vehicle/volvo-connected.yaml | 36 ++++---- util/providerauth.go | 65 ------------- vehicle/volvo-connected.go | 2 +- 18 files changed, 219 insertions(+), 208 deletions(-) rename server/{oauth2redirect/redirect.go => providerauth/providerauth.go} (66%) delete mode 100644 util/providerauth.go diff --git a/.storybook/preview.js b/.storybook/preview.js index 00e8bab4a..820374fa5 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.js @@ -37,6 +37,12 @@ const preview = { setup((app) => { app.config.globalProperties.$hiddenFeatures = () => true; app.use(setupI18n()); + + // Mock router-link for Storybook + app.component("router-link", { + props: ["to", "activeClass"], + template: '', + }); }); export default preview; diff --git a/api/api.go b/api/api.go index c4235bb83..fc70bf6b4 100644 --- a/api/api.go +++ b/api/api.go @@ -3,7 +3,7 @@ package api import ( "context" "io" - "net/http" + "net/url" "time" ) @@ -193,9 +193,11 @@ type Tariff interface { // AuthProvider is the ability to provide OAuth authentication through the ui type AuthProvider interface { - HandleCallback(r *http.Request) - HandleLogout(r *http.Request) - AuthCodeURL(state string) string + Login(state string) string + Logout() error + HandleCallback(responseValues url.Values) error + Authenticated() bool + DisplayName() string } // IconDescriber optionally provides an icon diff --git a/assets/js/components/Site/Site.vue b/assets/js/components/Site/Site.vue index 7e6e9293a..96c863c84 100644 --- a/assets/js/components/Site/Site.vue +++ b/assets/js/components/Site/Site.vue @@ -84,7 +84,7 @@ import collector from "@/mixins/collector.ts"; import WelcomeIcons from "./WelcomeIcons.vue"; import { defineComponent, type PropType } from "vue"; import type { - Auth, + AuthProviders, Battery, CURRENCY, Forecast, @@ -133,9 +133,7 @@ export default defineComponent({ bufferStartSoc: Number, siteTitle: String, vehicles: Object, - - auth: { type: Object as PropType, default: () => ({ vehicles: {} }) }, - + authProviders: { type: Object as PropType, default: () => ({}) }, currency: { type: String as PropType }, statistics: Object, tariffFeedIn: Number, @@ -195,7 +193,7 @@ export default defineComponent({ return Object.entries(vehicles).map(([name, vehicle]) => ({ name, ...vehicle })); }, topNavigation() { - return { vehicleLogins: this.auth.vehicles, ...this.collectProps(Navigation) }; + return this.collectProps(Navigation); }, showParkingLot() { // work in progess diff --git a/assets/js/components/Top/Header.vue b/assets/js/components/Top/Header.vue index ca5bbc4b1..8e09d6473 100644 --- a/assets/js/components/Top/Header.vue +++ b/assets/js/components/Top/Header.vue @@ -43,9 +43,8 @@ export default defineComponent({ title: String, }, computed: { - topNavigation() { - const vehicleLogins = store.state.auth ? store.state.auth.vehicles : {}; - return { vehicleLogins, ...this.collectProps(Navigation, store.state) }; + topNavigation(): any { + return this.collectProps(Navigation, store.state); }, }, }); diff --git a/assets/js/components/Top/Navigation.stories.ts b/assets/js/components/Top/Navigation.stories.ts index 5612fd9a0..6de8ddcba 100644 --- a/assets/js/components/Top/Navigation.stories.ts +++ b/assets/js/components/Top/Navigation.stories.ts @@ -8,7 +8,7 @@ export default { layout: "centered", }, argTypes: { - vehicleLogins: { control: "object" }, + authProviders: { control: "object" }, sponsor: { control: "object" }, }, } as Meta; @@ -24,30 +24,30 @@ const Template: StoryFn = (args) => ({ export const Standard = Template.bind({}); Standard.args = {}; -export const VehicleLogins = Template.bind({}); -VehicleLogins.args = { - vehicleLogins: { +export const OAuthStatus = Template.bind({}); +OAuthStatus.args = { + authProviders: { "Mercedes EQS": { authenticated: true, - uri: "https://login-provider-a.test/", + id: "mercedes-eqs-9oqwjdf9oqwjd", }, "Nissan Leaf Pro": { authenticated: true, - uri: "https://login-provider-b.test/", + id: "nissan-leaf-pro-9oqwjdf9oqwjd", }, }, }; -export const PendingVehicleLogins = Template.bind({}); -PendingVehicleLogins.args = { - vehicleLogins: { +export const PendingOAuthStatus = Template.bind({}); +PendingOAuthStatus.args = { + authProviders: { "Mercedes EQS": { authenticated: true, - uri: "https://login-provider-a.test/", + id: "mercedes-eqs-9oqwjdf9oqwjd", }, "Nissan Leaf Pro": { authenticated: false, - uri: "https://login-provider-b.test/", + id: "nissan-leaf-pro-9oqwjdf9oqwjd", }, }, }; diff --git a/assets/js/components/Top/Navigation.vue b/assets/js/components/Top/Navigation.vue index 11af67685..dd63c9442 100644 --- a/assets/js/components/Top/Navigation.vue +++ b/assets/js/components/Top/Navigation.vue @@ -9,7 +9,7 @@ data-testid="topnavigation-button" > @@ -61,7 +61,7 @@
  • @@ -75,9 +75,8 @@