Skip to content

Commit

Permalink
feature: add linkedin oauth provider
Browse files Browse the repository at this point in the history
  • Loading branch information
greenpau committed Mar 16, 2024
1 parent 08d3bc2 commit 5faadfc
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 10 deletions.
2 changes: 1 addition & 1 deletion pkg/idp/oauth/authenticate.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ func (b *IdentityProvider) Authenticate(r *requests.Request) error {
var m map[string]interface{}

switch b.config.Driver {
case "github", "gitlab", "facebook", "discord":
case "github", "gitlab", "facebook", "discord", "linkedin":
m, err = b.fetchClaims(accessToken)
if err != nil {
return errors.ErrIdentityProviderOauthFetchClaimsFailed.WithArgs(err)
Expand Down
11 changes: 9 additions & 2 deletions pkg/idp/oauth/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ type Config struct {
// LoginIcon is the UI login icon attributes.
LoginIcon *icons.LoginIcon `json:"login_icon,omitempty" xml:"login_icon,omitempty" yaml:"login_icon,omitempty"`

UserInfoFields []string `json:"user_info_fields,omitempty" xml:"user_info_fields,omitempty" yaml:"user_info_fields,omitempty"`
UserInfoRolesFieldName string `json:"user_info_roles_field_name,omitempty" xml:"user_info_roles_field_name,omitempty" yaml:"user_info_roles_field_name,omitempty"`
UserInfoFields []string `json:"user_info_fields,omitempty" xml:"user_info_fields,omitempty" yaml:"user_info_fields,omitempty"`
UserInfoRolesFieldName string `json:"user_info_roles_field_name,omitempty" xml:"user_info_roles_field_name,omitempty" yaml:"user_info_roles_field_name,omitempty"`

// The name of the cookie storing id_token from OAuth provider.
IdentityTokenCookieName string `json:"identity_token_cookie_name,omitempty" xml:"identity_token_cookie_name,omitempty" yaml:"identity_token_cookie_name,omitempty"`
Expand Down Expand Up @@ -161,6 +161,8 @@ func (cfg *Config) Validate() error {
cfg.Scopes = []string{"openid", "email", "profile"}
case "discord":
cfg.Scopes = []string{"identify"}
case "linkedin":
cfg.Scopes = []string{"openid", "email", "profile"}
default:
cfg.Scopes = []string{"openid", "email", "profile"}
}
Expand Down Expand Up @@ -250,6 +252,11 @@ func (cfg *Config) Validate() error {
cfg.AuthorizationURL = "https://discord.com/oauth2/authorize"
cfg.TokenURL = "https://discord.com/api/oauth2/token"
cfg.RequiredTokenFields = []string{"access_token"}
case "linkedin":
if cfg.BaseAuthURL == "" {
cfg.BaseAuthURL = "https://www.linkedin.com/oauth/"
cfg.MetadataURL = cfg.BaseAuthURL + ".well-known/openid-configuration"
}
case "generic":
case "":
return errors.ErrIdentityProviderConfig.WithArgs("driver name not found")
Expand Down
2 changes: 2 additions & 0 deletions pkg/idp/oauth/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,8 @@ func (b *IdentityProvider) Configure() error {
b.disableKeyVerification = true
b.disableNonce = true
b.enableAcceptHeader = true
case "linkedin":
b.disableNonce = true
case "nextcloud":
b.disableKeyVerification = true
}
Expand Down
30 changes: 23 additions & 7 deletions pkg/idp/oauth/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ func (b *IdentityProvider) fetchClaims(tokenData map[string]interface{}) (map[st
switch b.config.Driver {
case "github":
userURL = "https://api.github.com/user"
case "linkedin":
userURL = "https://api.linkedin.com/v2/userinfo"
case "gitlab":
userURL = b.userInfoURL
case "facebook":
Expand All @@ -137,7 +139,7 @@ func (b *IdentityProvider) fetchClaims(tokenData map[string]interface{}) (map[st

// Setup http request for the URL.
switch b.config.Driver {
case "github", "gitlab", "discord":
case "github", "gitlab", "discord", "linkedin":
req, err = http.NewRequest("GET", userURL, nil)
if err != nil {
return nil, err
Expand Down Expand Up @@ -165,7 +167,7 @@ func (b *IdentityProvider) fetchClaims(tokenData map[string]interface{}) (map[st
switch b.config.Driver {
case "github":
req.Header.Add("Authorization", "token "+tokenString)
case "gitlab", "discord":
case "gitlab", "discord", "linkedin":
req.Header.Add("Authorization", "Bearer "+tokenString)
}

Expand Down Expand Up @@ -193,6 +195,10 @@ func (b *IdentityProvider) fetchClaims(tokenData map[string]interface{}) (map[st
}

switch b.config.Driver {
case "linkedin":
if _, exists := data["sub"]; !exists {
return nil, fmt.Errorf("failed obtaining user profile with OAuth 2.0 access token, profile field not found")
}
case "gitlab":
if _, exists := data["profile"]; !exists {
return nil, fmt.Errorf("failed obtaining user profile with OAuth 2.0 access token, profile field not found")
Expand All @@ -205,7 +211,7 @@ func (b *IdentityProvider) fetchClaims(tokenData map[string]interface{}) (map[st
return nil, fmt.Errorf("failed obtaining user profile with OAuth 2.0 access token, login field not found")
}
case "discord":
if _,exists := data["id"]; !exists {
if _, exists := data["id"]; !exists {
return nil, fmt.Errorf("failed obtaining user profile with OAuth 2.0 access token, id field not found")
}
case "facebook":
Expand Down Expand Up @@ -367,6 +373,16 @@ func (b *IdentityProvider) fetchClaims(tokenData map[string]interface{}) (map[st
}
m["sub"] = data["id"]
m["name"] = data["name"]
case "linkedin":
for _, k := range []string{"name", "picture", "sub", "email"} {
if _, exists := data[k]; !exists {
continue
}
switch v := data[k].(type) {
case string:
m[k] = v
}
}
}

if len(userGroups) > 0 {
Expand All @@ -391,7 +407,7 @@ func (b *IdentityProvider) fetchDiscordGuilds(authToken string) (*userData, erro
return nil, err
}
req.Header.Set("Accept", "application/json")
req.Header.Add("Authorization", "Bearer " + authToken)
req.Header.Add("Authorization", "Bearer "+authToken)

// Fetch data from the URL.
resp, err := cli.Do(req)
Expand Down Expand Up @@ -443,9 +459,9 @@ func (b *IdentityProvider) fetchDiscordGuilds(authToken string) (*userData, erro
"Error converting Guild permissions to integer",
zap.Any("error", err),
)
} else if (perm & 0x08) == 0x08 { // Check for admin privileges
} else if (perm & 0x08) == 0x08 { // Check for admin privileges
data.Groups = append(data.Groups, fmt.Sprintf("discord.com/%s/admins", guildID))
}
}
}

data.Groups = append(data.Groups, fmt.Sprintf("discord.com/%s/members", guildID))
Expand All @@ -457,7 +473,7 @@ func (b *IdentityProvider) fetchDiscordGuilds(authToken string) (*userData, erro
return nil, err
}
req.Header.Set("Accept", "application/json")
req.Header.Add("Authorization", "Bearer " + authToken)
req.Header.Add("Authorization", "Bearer "+authToken)

resp, err = cli.Do(req)
if err != nil {
Expand Down

0 comments on commit 5faadfc

Please sign in to comment.