Skip to content

Commit

Permalink
fix: use linkedin oidc endpoint (#1254)
Browse files Browse the repository at this point in the history
## What kind of change does this PR introduce?
* Add OIDC support for the linkedin provider as highlighted
[here](https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin-v2#validating-id-tokens)
* Addresses #1216
  • Loading branch information
kangmingtay committed Sep 26, 2023
1 parent f40acfe commit 6d5c8eb
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 40 deletions.
63 changes: 25 additions & 38 deletions internal/api/provider/linkedin_oidc.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,20 @@ import (
"context"
"strings"

"github.com/coreos/go-oidc/v3/oidc"
"github.com/supabase/gotrue/internal/conf"
"golang.org/x/oauth2"
)

const (
defaultLinkedinOIDCAPIBase = "api.linkedin.com"
IssuerLinkedin = "https://www.linkedin.com"
)

type linkedinOIDCProvider struct {
*oauth2.Config
APIPath string
UserInfoURL string
UserEmailUrl string
}

// 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 {
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"`
oidc *oidc.Provider
APIPath string
}

// NewLinkedinOIDCProvider creates a Linkedin account provider via OIDC.
Expand All @@ -49,7 +38,16 @@ func NewLinkedinOIDCProvider(ext conf.OAuthProviderConfiguration, scopes string)
oauthScopes = append(oauthScopes, strings.Split(scopes, ",")...)
}

// Linkedin uses a different issuer from it's oidc discovery url
// https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin-v2#validating-id-tokens
ctx := oidc.InsecureIssuerURLContext(context.Background(), IssuerLinkedin)
oidcProvider, err := oidc.NewProvider(ctx, IssuerLinkedin+"/oauth")
if err != nil {
return nil, err
}

return &linkedinOIDCProvider{
oidc: oidcProvider,
Config: &oauth2.Config{
ClientID: ext.ClientID[0],
ClientSecret: ext.Secret,
Expand All @@ -69,29 +67,18 @@ func (g linkedinOIDCProvider) GetOAuthToken(code string) (*oauth2.Token, error)
}

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 {
return nil, err
idToken := tok.Extra("id_token")
if tok.AccessToken == "" || idToken == nil {
return &UserProvidedData{}, nil
}

return &UserProvidedData{
Metadata: &Claims{
Issuer: g.APIPath,
Subject: u.Sub,
Name: strings.TrimSpace(u.GivenName + " " + u.FamilyName),
Picture: u.Picture,
Email: u.Email,
EmailVerified: u.EmailVerified,

// To be deprecated
AvatarURL: u.Picture,
FullName: strings.TrimSpace(u.GivenName + " " + u.FamilyName),
ProviderId: u.Sub,
},
Emails: []Email{{
Email: u.Email,
Verified: u.EmailVerified,
Primary: true,
}},
}, nil
_, data, err := ParseIDToken(ctx, g.oidc, &oidc.Config{
ClientID: g.ClientID,
}, idToken.(string), ParseIDTokenOptions{
AccessToken: tok.AccessToken,
})
if err != nil {
return nil, err
}
return data, nil
}
50 changes: 48 additions & 2 deletions internal/api/provider/oidc.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"context"
"errors"
"fmt"
"strconv"
"strings"

"github.com/coreos/go-oidc/v3/oidc"
"github.com/golang-jwt/jwt"
Expand Down Expand Up @@ -43,10 +45,10 @@ func ParseIDToken(ctx context.Context, provider *oidc.Provider, config *oidc.Con
switch token.Issuer {
case IssuerGoogle:
token, data, err = parseGoogleIDToken(token)

case IssuerApple:
token, data, err = parseAppleIDToken(token)

case IssuerLinkedin:
token, data, err = parseLinkedinIDToken(token)
default:
token, data, err = parseGenericIDToken(token)
}
Expand Down Expand Up @@ -154,6 +156,50 @@ func parseAppleIDToken(token *oidc.IDToken) (*oidc.IDToken, *UserProvidedData, e
return token, &data, nil
}

type LinkedinIDTokenClaims struct {
jwt.StandardClaims

Email string `json:"email"`
EmailVerified string `json:"email_verified"`
FamilyName string `json:"family_name"`
GivenName string `json:"given_name"`
Locale string `json:"locale"`
Picture string `json:"picture"`
}

func parseLinkedinIDToken(token *oidc.IDToken) (*oidc.IDToken, *UserProvidedData, error) {
var claims LinkedinIDTokenClaims
if err := token.Claims(&claims); err != nil {
return nil, nil, err
}

var data UserProvidedData
emailVerified, err := strconv.ParseBool(claims.EmailVerified)
if err != nil {
return nil, nil, err
}
data.Emails = append(data.Emails, Email{
Email: claims.Email,
Verified: emailVerified,
Primary: true,
})

data.Metadata = &Claims{
Issuer: token.Issuer,
Subject: token.Subject,
Email: claims.Email,
EmailVerified: emailVerified,
Name: strings.TrimSpace(claims.GivenName + " " + claims.FamilyName),
GivenName: claims.GivenName,
FamilyName: claims.FamilyName,
Locale: claims.Locale,
Picture: claims.Picture,
ProviderId: token.Subject,
}

return token, &data, nil
}

func parseGenericIDToken(token *oidc.IDToken) (*oidc.IDToken, *UserProvidedData, error) {
var data UserProvidedData

Expand Down

0 comments on commit 6d5c8eb

Please sign in to comment.