# LinkedIn Kratos Flow
## Overview / Current contex
We are currently reliant on our own identity provider, namely Ory Kratos, which has served us well so far but has its own limitations. We would like to drive traffic + interactions from certain communities, starting with LinkedIn, and provide out-of-the-box integration of our platform with them.
We have created a PR to Ory Kratos with suggested LinkedIn implementation, but it has come with certain issues with it.
## Prerequisites
1. A LinkedIn app needs to be configured in https://www.linkedin.com/developers/apps
2. The app needs to be validated and allow r_emailaddress and r_liteprofile scopes:

3. Ory Kratos needs to have the linkedin app configured as an OIDC provider
```yml=
selfservice:
methods:
oidc:
enabled: true
config:
providers:
- id: linkedin
provider: linkedin
client_id: 77tjjnhvonojf4
client_secret: s66LUXd3nj2jxB4G
mapper_url: file:///etc/config/oidc.linkedin.jsonnet
scope:
- r_emailaddress
- r_liteprofile
```
5. Identity Schema needs to be updated so it maps first and lastname as claims:
```json=
local claims = {
email_verified: false
} + std.extVar('claims');
{
identity: {
traits: {
email: claims.email,
name:
{
first: claims.name,
last: claims.last_name,
}
},
},
}
```
NOTE: That sample doesn't include the profile picture. Currently, the picture is not part of the identity schema we support - we need to add it!
6. For local development - all mappings, port forwarding and configuration need to be configured + a callback on the mapped, external IP needs to be configured (this section will be expanded)
## Kratos LinkedIn Login Flow

[The Kratos Login flow with LinkedIn has the following steps](https://learn.microsoft.com/en-us/linkedin/shared/authentication/authorization-code-flow?context=linkedin%2Fcontext&tabs=HTTPS):
1. User navigates to the Alkemio login page and choses 'Sign in with Linkedin'
2. First time Alkemio users coming from LinkedIn need to authorize Alkemio access their core information used in LinkedIn, namely user email (r_emailaddress) and basic (lite) profile info (r_liteprofile). After the user authorizes the application, linkedin calls back the redirectURI configured in the request
3. User receives an authorization token. User makes a request to replace it with Access Token.
4. The authorization token is replaced with Access Token. It is returned to the user, together with its expiry time.
5. Kratos LinkedIn provider calls the introspection endpoint with HTTP post. The request is form post, following redirects. NOTE: This has bugs as the application sometimes receives 200 but "active:false" [which means the credentials are valid but do not match the client information in the token](https://learn.microsoft.com/en-us/linkedin/shared/authentication/token-introspection?context=linkedin%2Fcontext&tabs=curl). This step is executed in the `Introspection` function of provider_linkedin.go in kratos.
6. We check the scopes in the Kratos config vs the authorized scopes, returned by the introspection call, `scope` claim. This step is executed in the `Claims` function of provider_linkedin.go in kratos.
7. We get the user profile. The profile provides the picture with [URN format](https://learn.microsoft.com/en-us/linkedin/shared/api-guide/concepts/urns?.context=linkedin%2Fcontext). We will need an additional call to LinkedIn API to get the full profile picture details. This step is executed in the `Claims` function of provider_linkedin.go in kratos - `err = l.ApiCall(ProfileUrl, &profile, exchange)`
8. We get the user email. This step is executed in the `Claims` function of provider_linkedin.go in kratos - `err = l.ApiCall(EmailUrl, &emailaddress, exchange)`
## Building & Running
To build kratos container, from kratos root directory:
```bash=
DOCKER_BUILDKIT=1 docker build -f .docker/Dockerfile-build . -t [image_tag]
```
To run locally on external IP:
1. Check out server, client, kratos branches `linkedin-login-debug` (all repos need to be under one root folder, e.g. alkemio/kratos, alkemio/client-web, alkemio/server as there are relative paths)
2. Replace 78.90.161.46 everywhere with your IP
3. Server - copy .env.external vars in .env with your IP set
4. run `npm start:services:kratos:debug`
5. Start the server
6. Start the client
7. Check `Launch remote` debug configuration on kratos and start it. This attaches to the `pid` of the kratos container and you can remotely debug now. With every change the container is rebuild, the `pid` changes, so you will need to re-attach again.
## Progress
- [x] Create an image with more debug / trace data (vyanakiev/kratos:linkedin_debug_v0.2)
- [x] Document current state & findings
- [x] Add scope validation
- [ ] Expand identity schema on our side to include picture
- [ ] Add an additional call after step 8 to get the full profile picture
- [ ] Client side - fix mandatory accepted_terms. We need the flag on sign up with any provider
- [ ] Client side - fix issues when the flow is stopped in the middle + feedback to the user
- [ ] BUG: 2 users log in with the same account
- [ ] Re-configure test environment after all the changes + fixes
- [ ] Alkemio Server - extract picture
- [ ] Alkemio Server - make more resilient when identity scopes are missing
###### tags: `SSO`, 'Kratos', 'Auth', 'Identity', 'Authentication', 'LinkedIn', 'Social Sign-in'