From d306d1985753aa387fca1c99e31ef4bd3ec78ea9 Mon Sep 17 00:00:00 2001 From: Joachim Hill-Grannec Date: Thu, 14 Sep 2023 23:01:55 -0600 Subject: [PATCH 1/8] copy of original linkedin --- internal/api/provider/linkedin_oidc.go | 156 +++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 internal/api/provider/linkedin_oidc.go diff --git a/internal/api/provider/linkedin_oidc.go b/internal/api/provider/linkedin_oidc.go new file mode 100644 index 0000000000..9841cc5965 --- /dev/null +++ b/internal/api/provider/linkedin_oidc.go @@ -0,0 +1,156 @@ +package provider + +import ( + "context" + "errors" + "strings" + + "github.com/supabase/gotrue/internal/conf" + "golang.org/x/oauth2" +) + +const ( + defaultLinkedinAPIBase = "api.linkedin.com" +) + +type linkedinProvider struct { + *oauth2.Config + APIPath string + UserInfoURL string + UserEmailUrl string +} + +// See https://docs.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin?context=linkedin/consumer/context +// for retrieving a member's profile. This requires the r_liteprofile scope. +type linkedinUser struct { + ID string `json:"id"` + FirstName linkedinName `json:"firstName"` + LastName linkedinName `json:"lastName"` + AvatarURL struct { + DisplayImage struct { + Elements []struct { + Identifiers []struct { + Identifier string `json:"identifier"` + } `json:"identifiers"` + } `json:"elements"` + } `json:"displayImage~"` + } `json:"profilePicture"` +} + +func (u *linkedinUser) getAvatarUrl() string { + avatarURL := "" + if len(u.AvatarURL.DisplayImage.Elements) > 0 { + avatarURL = u.AvatarURL.DisplayImage.Elements[0].Identifiers[0].Identifier + } + return avatarURL +} + +type linkedinName struct { + Localized interface{} `json:"localized"` + PreferredLocale linkedinLocale `json:"preferredLocale"` +} + +type linkedinLocale struct { + Country string `json:"country"` + Language string `json:"language"` +} + +// See https://docs.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin?context=linkedin/consumer/context#retrieving-member-email-address +// for retrieving a member email address. This requires the r_email_address scope. +type linkedinElements struct { + Elements []struct { + Handle string `json:"handle"` + HandleTilde struct { + EmailAddress string `json:"emailAddress"` + } `json:"handle~"` + } `json:"elements"` +} + +// NewLinkedinProvider creates a Linkedin account provider. +func NewLinkedinProvider(ext conf.OAuthProviderConfiguration, scopes string) (OAuthProvider, error) { + if err := ext.ValidateOAuth(); err != nil { + return nil, err + } + + apiPath := chooseHost(ext.URL, defaultLinkedinAPIBase) + + oauthScopes := []string{ + "r_emailaddress", + "r_liteprofile", + } + + if scopes != "" { + oauthScopes = append(oauthScopes, strings.Split(scopes, ",")...) + } + + return &linkedinProvider{ + Config: &oauth2.Config{ + ClientID: ext.ClientID[0], + ClientSecret: ext.Secret, + Endpoint: oauth2.Endpoint{ + AuthURL: apiPath + "/oauth/v2/authorization", + TokenURL: apiPath + "/oauth/v2/accessToken", + }, + Scopes: oauthScopes, + RedirectURL: ext.RedirectURI, + }, + APIPath: apiPath, + }, nil +} + +func (g linkedinProvider) GetOAuthToken(code string) (*oauth2.Token, error) { + return g.Exchange(context.Background(), code) +} + +func GetName(name linkedinName) string { + key := name.PreferredLocale.Language + "_" + name.PreferredLocale.Country + myMap := name.Localized.(map[string]interface{}) + return myMap[key].(string) +} + +func (g linkedinProvider) GetUserData(ctx context.Context, tok *oauth2.Token) (*UserProvidedData, error) { + var u linkedinUser + if err := makeRequest(ctx, tok, g.Config, g.APIPath+"/v2/me?projection=(id,firstName,lastName,profilePicture(displayImage~:playableStreams))", &u); err != nil { + return nil, err + } + + var e linkedinElements + // Note: Use primary contact api for handling phone numbers + if err := makeRequest(ctx, tok, g.Config, g.APIPath+"/v2/emailAddress?q=members&projection=(elements*(handle~))", &e); err != nil { + return nil, err + } + + if len(e.Elements) <= 0 { + return nil, errors.New("unable to find email with Linkedin provider") + } + + emails := []Email{} + + if e.Elements[0].HandleTilde.EmailAddress != "" { + // linkedin only returns the primary email which is verified for the r_emailaddress scope. + emails = append(emails, Email{ + Email: e.Elements[0].HandleTilde.EmailAddress, + Primary: true, + Verified: true, + }) + } + + avatarURL := u.getAvatarUrl() + + return &UserProvidedData{ + Metadata: &Claims{ + Issuer: g.APIPath, + Subject: u.ID, + Name: strings.TrimSpace(GetName(u.FirstName) + " " + GetName(u.LastName)), + Picture: avatarURL, + Email: e.Elements[0].HandleTilde.EmailAddress, + EmailVerified: true, + + // To be deprecated + AvatarURL: avatarURL, + FullName: strings.TrimSpace(GetName(u.FirstName) + " " + GetName(u.LastName)), + ProviderId: u.ID, + }, + Emails: emails, + }, nil +} From f07c390120860d2b8390f8b63e35cfd00d934d62 Mon Sep 17 00:00:00 2001 From: Joachim Hill-Grannec Date: Thu, 14 Sep 2023 23:02:27 -0600 Subject: [PATCH 2/8] oidc refactor --- internal/api/provider/linkedin_oidc.go | 45 +++++++++++++------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/internal/api/provider/linkedin_oidc.go b/internal/api/provider/linkedin_oidc.go index 9841cc5965..7dc2705104 100644 --- a/internal/api/provider/linkedin_oidc.go +++ b/internal/api/provider/linkedin_oidc.go @@ -10,10 +10,10 @@ import ( ) const ( - defaultLinkedinAPIBase = "api.linkedin.com" + defaultLinkedinOIDCAPIBase = "api.linkedin.com" ) -type linkedinProvider struct { +type linkedinOIDCProvider struct { *oauth2.Config APIPath string UserInfoURL string @@ -22,10 +22,10 @@ type linkedinProvider struct { // See https://docs.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin?context=linkedin/consumer/context // for retrieving a member's profile. This requires the r_liteprofile scope. -type linkedinUser struct { - ID string `json:"id"` - FirstName linkedinName `json:"firstName"` - LastName linkedinName `json:"lastName"` +type linkedinOIDCUser struct { + ID string `json:"id"` + FirstName linkedinOIDCName `json:"firstName"` + LastName linkedinOIDCName `json:"lastName"` AvatarURL struct { DisplayImage struct { Elements []struct { @@ -37,7 +37,7 @@ type linkedinUser struct { } `json:"profilePicture"` } -func (u *linkedinUser) getAvatarUrl() string { +func (u *linkedinOIDCUser) getAvatarUrl() string { avatarURL := "" if len(u.AvatarURL.DisplayImage.Elements) > 0 { avatarURL = u.AvatarURL.DisplayImage.Elements[0].Identifiers[0].Identifier @@ -45,19 +45,19 @@ func (u *linkedinUser) getAvatarUrl() string { return avatarURL } -type linkedinName struct { - Localized interface{} `json:"localized"` - PreferredLocale linkedinLocale `json:"preferredLocale"` +type linkedinOIDCName struct { + Localized interface{} `json:"localized"` + PreferredLocale linkedinOIDCLocale `json:"preferredLocale"` } -type linkedinLocale struct { +type linkedinOIDCLocale struct { Country string `json:"country"` Language string `json:"language"` } // See https://docs.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin?context=linkedin/consumer/context#retrieving-member-email-address // for retrieving a member email address. This requires the r_email_address scope. -type linkedinElements struct { +type linkedinOIDCElements struct { Elements []struct { Handle string `json:"handle"` HandleTilde struct { @@ -67,12 +67,12 @@ type linkedinElements struct { } // NewLinkedinProvider creates a Linkedin account provider. -func NewLinkedinProvider(ext conf.OAuthProviderConfiguration, scopes string) (OAuthProvider, error) { +func NewLinkedinOIDCProvider(ext conf.OAuthProviderConfiguration, scopes string) (OAuthProvider, error) { if err := ext.ValidateOAuth(); err != nil { return nil, err } - apiPath := chooseHost(ext.URL, defaultLinkedinAPIBase) + apiPath := chooseHost(ext.URL, defaultLinkedinOIDCAPIBase) oauthScopes := []string{ "r_emailaddress", @@ -83,7 +83,7 @@ func NewLinkedinProvider(ext conf.OAuthProviderConfiguration, scopes string) (OA oauthScopes = append(oauthScopes, strings.Split(scopes, ",")...) } - return &linkedinProvider{ + return &linkedinOIDCProvider{ Config: &oauth2.Config{ ClientID: ext.ClientID[0], ClientSecret: ext.Secret, @@ -98,23 +98,22 @@ func NewLinkedinProvider(ext conf.OAuthProviderConfiguration, scopes string) (OA }, nil } -func (g linkedinProvider) GetOAuthToken(code string) (*oauth2.Token, error) { +func (g linkedinOIDCProvider) GetOAuthToken(code string) (*oauth2.Token, error) { return g.Exchange(context.Background(), code) } -func GetName(name linkedinName) string { +func GetOIDCName(name linkedinOIDCName) string { key := name.PreferredLocale.Language + "_" + name.PreferredLocale.Country myMap := name.Localized.(map[string]interface{}) return myMap[key].(string) } - -func (g linkedinProvider) GetUserData(ctx context.Context, tok *oauth2.Token) (*UserProvidedData, error) { - var u linkedinUser +func (g linkedinOIDCProvider) GetUserData(ctx context.Context, tok *oauth2.Token) (*UserProvidedData, error) { + var u linkedinOIDCUser if err := makeRequest(ctx, tok, g.Config, g.APIPath+"/v2/me?projection=(id,firstName,lastName,profilePicture(displayImage~:playableStreams))", &u); err != nil { return nil, err } - var e linkedinElements + var e linkedinOIDCElements // Note: Use primary contact api for handling phone numbers if err := makeRequest(ctx, tok, g.Config, g.APIPath+"/v2/emailAddress?q=members&projection=(elements*(handle~))", &e); err != nil { return nil, err @@ -141,14 +140,14 @@ func (g linkedinProvider) GetUserData(ctx context.Context, tok *oauth2.Token) (* Metadata: &Claims{ Issuer: g.APIPath, Subject: u.ID, - Name: strings.TrimSpace(GetName(u.FirstName) + " " + GetName(u.LastName)), + Name: strings.TrimSpace(GetOIDCName(u.FirstName) + " " + GetOIDCName(u.LastName)), Picture: avatarURL, Email: e.Elements[0].HandleTilde.EmailAddress, EmailVerified: true, // To be deprecated AvatarURL: avatarURL, - FullName: strings.TrimSpace(GetName(u.FirstName) + " " + GetName(u.LastName)), + FullName: strings.TrimSpace(GetOIDCName(u.FirstName) + " " + GetOIDCName(u.LastName)), ProviderId: u.ID, }, Emails: emails, From 65175ccaa5487ce113b53e054916021bb10ca4b2 Mon Sep 17 00:00:00 2001 From: Joachim Hill-Grannec Date: Thu, 14 Sep 2023 23:16:32 -0600 Subject: [PATCH 3/8] refactor to use OIDC and add new linkedin implementation --- internal/api/external.go | 2 + internal/api/provider/linkedin_oidc.go | 95 ++++++++------------------ internal/api/settings.go | 86 +++++++++++------------ internal/conf/configuration.go | 1 + 4 files changed, 75 insertions(+), 109 deletions(-) diff --git a/internal/api/external.go b/internal/api/external.go index 7ad21f851f..eda197bbc7 100644 --- a/internal/api/external.go +++ b/internal/api/external.go @@ -540,6 +540,8 @@ func (a *API) Provider(ctx context.Context, name string, scopes string) (provide return provider.NewKeycloakProvider(config.External.Keycloak, scopes) case "linkedin": return provider.NewLinkedinProvider(config.External.Linkedin, scopes) + case "linkedinOIDC": + return provider.NewLinkedinOIDCProvider(config.External.LinkedinOIDC, scopes) case "notion": return provider.NewNotionProvider(config.External.Notion) case "spotify": diff --git a/internal/api/provider/linkedin_oidc.go b/internal/api/provider/linkedin_oidc.go index 7dc2705104..ada7a12f86 100644 --- a/internal/api/provider/linkedin_oidc.go +++ b/internal/api/provider/linkedin_oidc.go @@ -2,7 +2,6 @@ package provider import ( "context" - "errors" "strings" "github.com/supabase/gotrue/internal/conf" @@ -20,29 +19,20 @@ type linkedinOIDCProvider struct { UserEmailUrl string } -// See https://docs.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin?context=linkedin/consumer/context -// for retrieving a member's profile. This requires the r_liteprofile scope. +// See https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin-v2 +// for retrieving a member's profile. This requires the profile, openid, and email scope. type linkedinOIDCUser struct { - ID string `json:"id"` - FirstName linkedinOIDCName `json:"firstName"` - LastName linkedinOIDCName `json:"lastName"` - AvatarURL struct { - DisplayImage struct { - Elements []struct { - Identifiers []struct { - Identifier string `json:"identifier"` - } `json:"identifiers"` - } `json:"elements"` - } `json:"displayImage~"` - } `json:"profilePicture"` + Sub string `json:"sub"` + Email string `json:"email"` + Name string `json:"name"` + Picture string `json:"picture"` + GivenName string `json:"given_name"` + FamilyName string `json:"family_name"` + EmailVerified bool `json:"email_verified"` } func (u *linkedinOIDCUser) getAvatarUrl() string { - avatarURL := "" - if len(u.AvatarURL.DisplayImage.Elements) > 0 { - avatarURL = u.AvatarURL.DisplayImage.Elements[0].Identifiers[0].Identifier - } - return avatarURL + return u.Picture } type linkedinOIDCName struct { @@ -55,18 +45,7 @@ type linkedinOIDCLocale struct { Language string `json:"language"` } -// See https://docs.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin?context=linkedin/consumer/context#retrieving-member-email-address -// for retrieving a member email address. This requires the r_email_address scope. -type linkedinOIDCElements struct { - Elements []struct { - Handle string `json:"handle"` - HandleTilde struct { - EmailAddress string `json:"emailAddress"` - } `json:"handle~"` - } `json:"elements"` -} - -// NewLinkedinProvider creates a Linkedin account provider. +// NewLinkedinOIDCProvider creates a Linkedin account provider via OIDC. func NewLinkedinOIDCProvider(ext conf.OAuthProviderConfiguration, scopes string) (OAuthProvider, error) { if err := ext.ValidateOAuth(); err != nil { return nil, err @@ -75,8 +54,9 @@ func NewLinkedinOIDCProvider(ext conf.OAuthProviderConfiguration, scopes string) apiPath := chooseHost(ext.URL, defaultLinkedinOIDCAPIBase) oauthScopes := []string{ - "r_emailaddress", - "r_liteprofile", + "openid", + "email", + "profile", } if scopes != "" { @@ -109,47 +89,28 @@ func GetOIDCName(name linkedinOIDCName) string { } func (g linkedinOIDCProvider) GetUserData(ctx context.Context, tok *oauth2.Token) (*UserProvidedData, error) { var u linkedinOIDCUser - if err := makeRequest(ctx, tok, g.Config, g.APIPath+"/v2/me?projection=(id,firstName,lastName,profilePicture(displayImage~:playableStreams))", &u); err != nil { + if err := makeRequest(ctx, tok, g.Config, g.APIPath+"/v2/userinfo", &u); err != nil { return nil, err } - var e linkedinOIDCElements - // Note: Use primary contact api for handling phone numbers - if err := makeRequest(ctx, tok, g.Config, g.APIPath+"/v2/emailAddress?q=members&projection=(elements*(handle~))", &e); err != nil { - return nil, err - } - - if len(e.Elements) <= 0 { - return nil, errors.New("unable to find email with Linkedin provider") - } - - emails := []Email{} - - if e.Elements[0].HandleTilde.EmailAddress != "" { - // linkedin only returns the primary email which is verified for the r_emailaddress scope. - emails = append(emails, Email{ - Email: e.Elements[0].HandleTilde.EmailAddress, - Primary: true, - Verified: true, - }) - } - - avatarURL := u.getAvatarUrl() - return &UserProvidedData{ Metadata: &Claims{ Issuer: g.APIPath, - Subject: u.ID, - Name: strings.TrimSpace(GetOIDCName(u.FirstName) + " " + GetOIDCName(u.LastName)), - Picture: avatarURL, - Email: e.Elements[0].HandleTilde.EmailAddress, - EmailVerified: true, + Subject: u.Sub, + Name: strings.TrimSpace(u.GivenName + " " + u.FamilyName), + Picture: u.Picture, + Email: u.Email, + EmailVerified: u.EmailVerified, // To be deprecated - AvatarURL: avatarURL, - FullName: strings.TrimSpace(GetOIDCName(u.FirstName) + " " + GetOIDCName(u.LastName)), - ProviderId: u.ID, + AvatarURL: u.Picture, + FullName: strings.TrimSpace(u.GivenName + " " + u.FamilyName), + ProviderId: u.Sub, }, - Emails: emails, + Emails: []Email{{ + Email: u.Email, + Verified: u.EmailVerified, + Primary: true, + }}, }, nil } diff --git a/internal/api/settings.go b/internal/api/settings.go index a24690eb31..8c7b603855 100644 --- a/internal/api/settings.go +++ b/internal/api/settings.go @@ -3,27 +3,28 @@ package api import "net/http" type ProviderSettings struct { - Apple bool `json:"apple"` - Azure bool `json:"azure"` - Bitbucket bool `json:"bitbucket"` - Discord bool `json:"discord"` - Facebook bool `json:"facebook"` - Figma bool `json:"figma"` - GitHub bool `json:"github"` - GitLab bool `json:"gitlab"` - Google bool `json:"google"` - Keycloak bool `json:"keycloak"` - Kakao bool `json:"kakao"` - Linkedin bool `json:"linkedin"` - Notion bool `json:"notion"` - Spotify bool `json:"spotify"` - Slack bool `json:"slack"` - WorkOS bool `json:"workos"` - Twitch bool `json:"twitch"` - Twitter bool `json:"twitter"` - Email bool `json:"email"` - Phone bool `json:"phone"` - Zoom bool `json:"zoom"` + Apple bool `json:"apple"` + Azure bool `json:"azure"` + Bitbucket bool `json:"bitbucket"` + Discord bool `json:"discord"` + Facebook bool `json:"facebook"` + Figma bool `json:"figma"` + GitHub bool `json:"github"` + GitLab bool `json:"gitlab"` + Google bool `json:"google"` + Keycloak bool `json:"keycloak"` + Kakao bool `json:"kakao"` + Linkedin bool `json:"linkedin"` + LinkedinOIDC bool `json:"linkedinOIDC"` + Notion bool `json:"notion"` + Spotify bool `json:"spotify"` + Slack bool `json:"slack"` + WorkOS bool `json:"workos"` + Twitch bool `json:"twitch"` + Twitter bool `json:"twitter"` + Email bool `json:"email"` + Phone bool `json:"phone"` + Zoom bool `json:"zoom"` } type Settings struct { @@ -41,27 +42,28 @@ func (a *API) Settings(w http.ResponseWriter, r *http.Request) error { return sendJSON(w, http.StatusOK, &Settings{ ExternalProviders: ProviderSettings{ - Apple: config.External.Apple.Enabled, - Azure: config.External.Azure.Enabled, - Bitbucket: config.External.Bitbucket.Enabled, - Discord: config.External.Discord.Enabled, - Facebook: config.External.Facebook.Enabled, - Figma: config.External.Figma.Enabled, - GitHub: config.External.Github.Enabled, - GitLab: config.External.Gitlab.Enabled, - Google: config.External.Google.Enabled, - Kakao: config.External.Kakao.Enabled, - Keycloak: config.External.Keycloak.Enabled, - Linkedin: config.External.Linkedin.Enabled, - Notion: config.External.Notion.Enabled, - Spotify: config.External.Spotify.Enabled, - Slack: config.External.Slack.Enabled, - Twitch: config.External.Twitch.Enabled, - Twitter: config.External.Twitter.Enabled, - WorkOS: config.External.WorkOS.Enabled, - Email: config.External.Email.Enabled, - Phone: config.External.Phone.Enabled, - Zoom: config.External.Zoom.Enabled, + Apple: config.External.Apple.Enabled, + Azure: config.External.Azure.Enabled, + Bitbucket: config.External.Bitbucket.Enabled, + Discord: config.External.Discord.Enabled, + Facebook: config.External.Facebook.Enabled, + Figma: config.External.Figma.Enabled, + GitHub: config.External.Github.Enabled, + GitLab: config.External.Gitlab.Enabled, + Google: config.External.Google.Enabled, + Kakao: config.External.Kakao.Enabled, + Keycloak: config.External.Keycloak.Enabled, + Linkedin: config.External.Linkedin.Enabled, + LinkedinOIDC: config.External.LinkedinOIDC.Enabled, + Notion: config.External.Notion.Enabled, + Spotify: config.External.Spotify.Enabled, + Slack: config.External.Slack.Enabled, + Twitch: config.External.Twitch.Enabled, + Twitter: config.External.Twitter.Enabled, + WorkOS: config.External.WorkOS.Enabled, + Email: config.External.Email.Enabled, + Phone: config.External.Phone.Enabled, + Zoom: config.External.Zoom.Enabled, }, DisableSignup: config.DisableSignup, diff --git a/internal/conf/configuration.go b/internal/conf/configuration.go index 588b53ccae..b9dc57a0b3 100644 --- a/internal/conf/configuration.go +++ b/internal/conf/configuration.go @@ -196,6 +196,7 @@ type ProviderConfiguration struct { Notion OAuthProviderConfiguration `json:"notion"` Keycloak OAuthProviderConfiguration `json:"keycloak"` Linkedin OAuthProviderConfiguration `json:"linkedin"` + LinkedinOIDC OAuthProviderConfiguration `json:"linkedinOIDC"` Spotify OAuthProviderConfiguration `json:"spotify"` Slack OAuthProviderConfiguration `json:"slack"` Twitter OAuthProviderConfiguration `json:"twitter"` From 20c36d3a2305f9b33cc95f5134d0d5a046a0e0b1 Mon Sep 17 00:00:00 2001 From: Joachim Hill-Grannec Date: Fri, 15 Sep 2023 09:41:20 -0600 Subject: [PATCH 4/8] removed unused types and func --- internal/api/provider/linkedin_oidc.go | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/internal/api/provider/linkedin_oidc.go b/internal/api/provider/linkedin_oidc.go index ada7a12f86..49fde48e20 100644 --- a/internal/api/provider/linkedin_oidc.go +++ b/internal/api/provider/linkedin_oidc.go @@ -35,16 +35,6 @@ func (u *linkedinOIDCUser) getAvatarUrl() string { return u.Picture } -type linkedinOIDCName struct { - Localized interface{} `json:"localized"` - PreferredLocale linkedinOIDCLocale `json:"preferredLocale"` -} - -type linkedinOIDCLocale struct { - Country string `json:"country"` - Language string `json:"language"` -} - // NewLinkedinOIDCProvider creates a Linkedin account provider via OIDC. func NewLinkedinOIDCProvider(ext conf.OAuthProviderConfiguration, scopes string) (OAuthProvider, error) { if err := ext.ValidateOAuth(); err != nil { @@ -82,11 +72,6 @@ func (g linkedinOIDCProvider) GetOAuthToken(code string) (*oauth2.Token, error) return g.Exchange(context.Background(), code) } -func GetOIDCName(name linkedinOIDCName) string { - key := name.PreferredLocale.Language + "_" + name.PreferredLocale.Country - myMap := name.Localized.(map[string]interface{}) - return myMap[key].(string) -} func (g linkedinOIDCProvider) GetUserData(ctx context.Context, tok *oauth2.Token) (*UserProvidedData, error) { var u linkedinOIDCUser if err := makeRequest(ctx, tok, g.Config, g.APIPath+"/v2/userinfo", &u); err != nil { From ba83ad45a2cee5e3c9dd10f006f6faf6602d9421 Mon Sep 17 00:00:00 2001 From: Joachim Hill-Grannec Date: Fri, 15 Sep 2023 10:09:29 -0600 Subject: [PATCH 5/8] provider in url is always lower case --- internal/api/external.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/api/external.go b/internal/api/external.go index eda197bbc7..38b14b26f2 100644 --- a/internal/api/external.go +++ b/internal/api/external.go @@ -540,7 +540,7 @@ func (a *API) Provider(ctx context.Context, name string, scopes string) (provide return provider.NewKeycloakProvider(config.External.Keycloak, scopes) case "linkedin": return provider.NewLinkedinProvider(config.External.Linkedin, scopes) - case "linkedinOIDC": + case "linkedin-oidc": return provider.NewLinkedinOIDCProvider(config.External.LinkedinOIDC, scopes) case "notion": return provider.NewNotionProvider(config.External.Notion) From 8436be41ca47b17b59ea91aa441ebd0886dc10da Mon Sep 17 00:00:00 2001 From: Joachim Hill-Grannec Date: Thu, 21 Sep 2023 09:35:59 -0600 Subject: [PATCH 6/8] static fix - unused function json convention fix --- internal/api/provider/linkedin_oidc.go | 4 ---- internal/api/settings.go | 2 +- internal/conf/configuration.go | 2 +- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/internal/api/provider/linkedin_oidc.go b/internal/api/provider/linkedin_oidc.go index 49fde48e20..48c1a14a85 100644 --- a/internal/api/provider/linkedin_oidc.go +++ b/internal/api/provider/linkedin_oidc.go @@ -31,10 +31,6 @@ type linkedinOIDCUser struct { EmailVerified bool `json:"email_verified"` } -func (u *linkedinOIDCUser) getAvatarUrl() string { - return u.Picture -} - // NewLinkedinOIDCProvider creates a Linkedin account provider via OIDC. func NewLinkedinOIDCProvider(ext conf.OAuthProviderConfiguration, scopes string) (OAuthProvider, error) { if err := ext.ValidateOAuth(); err != nil { diff --git a/internal/api/settings.go b/internal/api/settings.go index 8c7b603855..d9517d2b3d 100644 --- a/internal/api/settings.go +++ b/internal/api/settings.go @@ -15,7 +15,7 @@ type ProviderSettings struct { Keycloak bool `json:"keycloak"` Kakao bool `json:"kakao"` Linkedin bool `json:"linkedin"` - LinkedinOIDC bool `json:"linkedinOIDC"` + LinkedinOIDC bool `json:"linkedin_oidc"` Notion bool `json:"notion"` Spotify bool `json:"spotify"` Slack bool `json:"slack"` diff --git a/internal/conf/configuration.go b/internal/conf/configuration.go index b9dc57a0b3..c7ba2284a3 100644 --- a/internal/conf/configuration.go +++ b/internal/conf/configuration.go @@ -196,7 +196,7 @@ type ProviderConfiguration struct { Notion OAuthProviderConfiguration `json:"notion"` Keycloak OAuthProviderConfiguration `json:"keycloak"` Linkedin OAuthProviderConfiguration `json:"linkedin"` - LinkedinOIDC OAuthProviderConfiguration `json:"linkedinOIDC"` + LinkedinOIDC OAuthProviderConfiguration `json:"linkedin_oidc"` Spotify OAuthProviderConfiguration `json:"spotify"` Slack OAuthProviderConfiguration `json:"slack"` Twitter OAuthProviderConfiguration `json:"twitter"` From 4f3658916aa1db342214e9743ba8a5ba451e427d Mon Sep 17 00:00:00 2001 From: Kang Ming Date: Mon, 25 Sep 2023 13:14:56 -0700 Subject: [PATCH 7/8] Update internal/api/external.go --- internal/api/external.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/api/external.go b/internal/api/external.go index 38b14b26f2..6d8bed8c20 100644 --- a/internal/api/external.go +++ b/internal/api/external.go @@ -540,7 +540,7 @@ func (a *API) Provider(ctx context.Context, name string, scopes string) (provide return provider.NewKeycloakProvider(config.External.Keycloak, scopes) case "linkedin": return provider.NewLinkedinProvider(config.External.Linkedin, scopes) - case "linkedin-oidc": + case "linkedin_oidc": return provider.NewLinkedinOIDCProvider(config.External.LinkedinOIDC, scopes) case "notion": return provider.NewNotionProvider(config.External.Notion) From cc1e0e2b5c6706606bbf38947a51e7d46e0bfa2b Mon Sep 17 00:00:00 2001 From: Kang Ming Date: Mon, 25 Sep 2023 13:15:03 -0700 Subject: [PATCH 8/8] Update internal/conf/configuration.go --- internal/conf/configuration.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/conf/configuration.go b/internal/conf/configuration.go index c7ba2284a3..f2846e2815 100644 --- a/internal/conf/configuration.go +++ b/internal/conf/configuration.go @@ -196,7 +196,7 @@ type ProviderConfiguration struct { Notion OAuthProviderConfiguration `json:"notion"` Keycloak OAuthProviderConfiguration `json:"keycloak"` Linkedin OAuthProviderConfiguration `json:"linkedin"` - LinkedinOIDC OAuthProviderConfiguration `json:"linkedin_oidc"` + LinkedinOIDC OAuthProviderConfiguration `json:"linkedin_oidc" envconfig:"LINKEDIN_OIDC"` Spotify OAuthProviderConfiguration `json:"spotify"` Slack OAuthProviderConfiguration `json:"slack"` Twitter OAuthProviderConfiguration `json:"twitter"`