Some checks failed
CI / Run tests and lints (push) Failing after 29s
CI / Build CLI docs (push) Has been skipped
CI / Run Playwright end-to-end tests (mysql) (push) Failing after 3s
CI / Run Playwright end-to-end tests (postgres) (push) Failing after 2s
CI / Run Playwright end-to-end tests (sqlite) (push) Failing after 3s
CodeQL / Analyze (go) (push) Failing after 4s
CodeQL / Analyze (javascript) (push) Failing after 3s
Docker Image Scanners / scanners (push) Failing after 4s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Run end-to-end tests (postgres) (push) Failing after 19s
CI / Run end-to-end tests (sqlite) (push) Failing after 2s
CI / Run Playwright end-to-end tests (cockroach) (push) Failing after 2s
CI / Draft newsletter (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
210 lines
6.9 KiB
Go
210 lines
6.9 KiB
Go
// Copyright © 2023 Ory Corp
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package oidc_test
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/url"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/hashicorp/go-retryablehttp"
|
|
"github.com/jarcoal/httpmock"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"golang.org/x/oauth2"
|
|
|
|
"github.com/ory/kratos/pkg"
|
|
"github.com/ory/kratos/selfservice/strategy/oidc"
|
|
)
|
|
|
|
func TestProviderLinkedin_Claims(t *testing.T) {
|
|
_, base := pkg.NewFastRegistryWithMocks(t)
|
|
reg := &mockRegistry{base, retryablehttp.NewClient()}
|
|
httpmock.ActivateNonDefault(reg.cl.HTTPClient)
|
|
t.Cleanup(httpmock.DeactivateAndReset)
|
|
|
|
httpmock.RegisterResponder("GET", "https://api.linkedin.com/v2/me?projection=(id,localizedFirstName,localizedLastName,profilePicture(displayImage~digitalmediaAsset:playableStreams))",
|
|
func(req *http.Request) (*http.Response, error) {
|
|
resp, err := httpmock.NewJsonResponse(200, map[string]interface{}{
|
|
"localizedLastName": "Doe",
|
|
"profilePicture": map[string]interface{}{
|
|
"displayImage": "urn:li:digitalmediaAsset:D5F04AQG1h-anJtsLeA",
|
|
"displayImage~": map[string]interface{}{
|
|
"elements": []map[string]interface{}{
|
|
{
|
|
"identifiers": []map[string]interface{}{
|
|
{
|
|
"identifier": "https://media-exp1.licdn.com/dms/image/D5F04AQG1h-anJtsLeA/profile-displayphoto-shrink_100_100/0/1661502148285?e=1674086400&v=beta&t=5zdsvAqGS1idrfNAai-zwp01K4hApGeJyRV8zBTjZck",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
"identifiers": []map[string]interface{}{
|
|
{
|
|
"identifier": "https://media-exp1.licdn.com/dms/image/D5F04AQG1h-anJtsLeA/profile-displayphoto-shrink_200_200/0/1661502148285?e=1674086400&v=beta&t=fbHprGci4vEpeQdzRTyt9YJjNXTgv-14h9ROr_DE5R4",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
"identifiers": []map[string]interface{}{
|
|
{
|
|
"identifier": "https://media-exp1.licdn.com/dms/image/D5F04AQG1h-anJtsLeA/profile-displayphoto-shrink_400_400/0/1661502148285?e=1674086400&v=beta&t=SMgXuiZ5DiiPf4iMrTPQWoCnFRh4Y-DfoWjopUZ0qCo",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
"identifiers": []map[string]interface{}{
|
|
{
|
|
"identifier": "https://media-exp1.licdn.com/dms/image/D5F04AQG1h-anJtsLeA/profile-displayphoto-shrink_800_800/0/1661502148285?e=1674086400&v=beta&t=QzC9b32DmLRIwU7P27LfJUqglXsJm056WkA_TTOHxHU",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"localizedFirstName": "John",
|
|
"id": "5foOWOiYXD",
|
|
})
|
|
return resp, err
|
|
})
|
|
|
|
httpmock.RegisterResponder("GET", "https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))",
|
|
func(req *http.Request) (*http.Response, error) {
|
|
resp, err := httpmock.NewJsonResponse(200, map[string]interface{}{
|
|
"elements": []map[string]interface{}{
|
|
{
|
|
"handle~": map[string]interface{}{
|
|
"emailAddress": "john.doe@gmail.com",
|
|
},
|
|
"handle": "urn:li:emailAddress:-1810278278",
|
|
},
|
|
},
|
|
})
|
|
return resp, err
|
|
},
|
|
)
|
|
|
|
httpmock.RegisterResponder("POST", "https://www.linkedin.com/oauth/v2/introspectToken",
|
|
func(req *http.Request) (*http.Response, error) {
|
|
resp, err := httpmock.NewJsonResponse(200, map[string]interface{}{
|
|
"active": true,
|
|
"client_id": "78e533g1dq0rs5",
|
|
"authorized_at": 1668518671,
|
|
"created_at": 1668518671,
|
|
"status": "active",
|
|
"expires_at": 2673702676,
|
|
"scope": "r_emailaddress,r_liteprofile",
|
|
"auth_type": "3L",
|
|
})
|
|
return resp, err
|
|
},
|
|
)
|
|
|
|
c := &oidc.Configuration{
|
|
ID: "linkedin",
|
|
Provider: "linkedin",
|
|
ClientID: "abcd",
|
|
ClientSecret: "secret",
|
|
Mapper: "file://./stub/oidc.linkedin.jsonnet",
|
|
Scope: []string{"r_emailaddress", "r_liteprofile"},
|
|
}
|
|
linkedin := oidc.NewProviderLinkedIn(c, reg)
|
|
|
|
const fakeLinkedinIDToken = "id_token_mock_" // #nosec G101 test code
|
|
actual, err := linkedin.(oidc.OAuth2Provider).Claims(
|
|
context.Background(),
|
|
(&oauth2.Token{AccessToken: "foo", Expiry: time.Now().Add(time.Hour)}).WithExtra(map[string]interface{}{"id_token": fakeLinkedinIDToken}),
|
|
url.Values{},
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, &oidc.Claims{
|
|
Issuer: "https://login.linkedin.com/",
|
|
Subject: "5foOWOiYXD",
|
|
GivenName: "John",
|
|
LastName: "Doe",
|
|
Email: "john.doe@gmail.com",
|
|
Picture: "https://media-exp1.licdn.com/dms/image/D5F04AQG1h-anJtsLeA/profile-displayphoto-shrink_400_400/0/1661502148285?e=1674086400&v=beta&t=SMgXuiZ5DiiPf4iMrTPQWoCnFRh4Y-DfoWjopUZ0qCo",
|
|
}, actual)
|
|
}
|
|
|
|
func TestProviderLinkedin_No_Picture(t *testing.T) {
|
|
_, base := pkg.NewFastRegistryWithMocks(t)
|
|
reg := &mockRegistry{base, retryablehttp.NewClient()}
|
|
httpmock.ActivateNonDefault(reg.cl.HTTPClient)
|
|
t.Cleanup(httpmock.DeactivateAndReset)
|
|
|
|
httpmock.RegisterResponder("GET", "https://api.linkedin.com/v2/me?projection=(id,localizedFirstName,localizedLastName,profilePicture(displayImage~digitalmediaAsset:playableStreams))",
|
|
func(req *http.Request) (*http.Response, error) {
|
|
resp, err := httpmock.NewJsonResponse(200, map[string]interface{}{
|
|
"localizedLastName": "Doe",
|
|
"localizedFirstName": "John",
|
|
"id": "5foOWOiYXD",
|
|
})
|
|
return resp, err
|
|
})
|
|
|
|
httpmock.RegisterResponder("GET", "https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))",
|
|
func(req *http.Request) (*http.Response, error) {
|
|
resp, err := httpmock.NewJsonResponse(200, map[string]interface{}{
|
|
"elements": []map[string]interface{}{
|
|
{
|
|
"handle~": map[string]interface{}{
|
|
"emailAddress": "john.doe@gmail.com",
|
|
},
|
|
"handle": "urn:li:emailAddress:-1810278278",
|
|
},
|
|
},
|
|
})
|
|
return resp, err
|
|
},
|
|
)
|
|
|
|
httpmock.RegisterResponder("POST", "https://www.linkedin.com/oauth/v2/introspectToken",
|
|
func(req *http.Request) (*http.Response, error) {
|
|
resp, err := httpmock.NewJsonResponse(200, map[string]interface{}{
|
|
"active": true,
|
|
"client_id": "78e533g1dq0rs5",
|
|
"authorized_at": 1668518671,
|
|
"created_at": 1668518671,
|
|
"status": "active",
|
|
"expires_at": 2673702676,
|
|
"scope": "r_emailaddress,r_liteprofile",
|
|
"auth_type": "3L",
|
|
})
|
|
return resp, err
|
|
},
|
|
)
|
|
|
|
c := &oidc.Configuration{
|
|
ID: "linkedin",
|
|
Provider: "linkedin",
|
|
ClientID: "abcd",
|
|
ClientSecret: "secret",
|
|
Mapper: "file://./stub/oidc.linkedin.jsonnet",
|
|
Scope: []string{"r_emailaddress", "r_liteprofile"},
|
|
}
|
|
linkedin := oidc.NewProviderLinkedIn(c, reg)
|
|
|
|
const fakeLinkedinIDToken = "id_token_mock_" // #nosec G101 test code
|
|
actual, err := linkedin.(oidc.OAuth2Provider).Claims(
|
|
context.Background(),
|
|
(&oauth2.Token{AccessToken: "foo", Expiry: time.Now().Add(time.Hour)}).WithExtra(map[string]interface{}{"id_token": fakeLinkedinIDToken}),
|
|
url.Values{},
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, &oidc.Claims{
|
|
Issuer: "https://login.linkedin.com/",
|
|
Subject: "5foOWOiYXD",
|
|
GivenName: "John",
|
|
LastName: "Doe",
|
|
Email: "john.doe@gmail.com",
|
|
Picture: "",
|
|
}, actual)
|
|
}
|