Skip to content

Commit

Permalink
feat: support GitLab OIDC Provider (#519)
Browse files Browse the repository at this point in the history
Closes #518

Co-authored-by: hackerman <3372410+aeneasr@users.noreply.github.com>
  • Loading branch information
perryao and aeneasr committed Oct 13, 2020
1 parent bf94a40 commit 8580d96
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 2 deletions.
3 changes: 2 additions & 1 deletion .schema/config.schema.json
Expand Up @@ -143,10 +143,11 @@
},
"provider": {
"title": "Provider",
"description": "Can be one of github, generic, google, microsoft.",
"description": "Can be one of github, gitlab, generic, google, microsoft.",
"type": "string",
"enum": [
"github",
"gitlab",
"generic",
"google",
"microsoft"
Expand Down
@@ -1,6 +1,6 @@
---
id: sign-in-with-github-google-facebook-linkedin
title: Sign in with GitHub, Google, Facebook, LinkedIn, Microsoft ...
title: Sign in with GitHub, GitLab, Google, Facebook, LinkedIn, Microsoft ...
---

In this document we will take a look at setting up "Sign in with GitHub" using
Expand Down Expand Up @@ -126,6 +126,73 @@ selfservice:
Next, open the login endpoint of the SecureApp and you should see the GitHub
Login option!

## GitLab

To set up "Sign in with GitLab" you must create a [GitLab OAuth2 Application](https://docs.gitlab.com/ee/integration/oauth_provider.html#adding-an-application-through-the-profile).

Set the "Redirect URI" to:

```
http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/strategies/oidc/callback/gitlab
```

The pattern of this URL is:

```
http(s)://<domain-of-ory-kratos>:<public-port>/self-service/browser/flows/strategies/oidc/callback/<provider-id>
```

:::note
While you can use [GitLab as an OIDC identity provider](https://docs.gitlab.com/ee/integration/openid_connect_provider.html),
GitLab only returns the sub and sub_legacy claims in the ID token.
Therefore, ORY Kratos makes a request to [GitLab's /oauth/userinfo API](https://gitlab.com/oauth/userinfo) and adds the user info to `std.extVar('claims')`.
:::

```json title="contrib/quickstart/kratos/email-password/oidc.gitlab.jsonnet"
local claims = {
email_verified: false
} + std.extVar('claims');

{
identity: {
traits: {
// Allowing unverified email addresses enables account
// enumeration attacks, especially if the value is used for
// e.g. verification or as a password login identifier.
//
// Therefore we only return the email if it (a) exists and (b) is marked verified
// by GitLab.
[if "email" in claims && claims.email_verified then "email" else null]: claims.email,
},
},
}
```

Now, enable the GitLab provider in the ORY Kratos config located at
`<kratos-directory>/contrib/quickstart/kratos/email-password/.kratos.yml`.

```yaml title="contrib/quickstart/kratos/email-password/.kratos.yml"
# $ kratos -c path/to/my/kratos/config.yml serve
selfservice:
strategies:
oidc:
enabled: true
config:
providers:
- id: gitlab # this is `<provider-id>` in the Authorization callback URL. DO NOT CHANGE IT ONCE SET!
provider: gitlab
client_id: .... # Replace this with the OAuth2 Client ID provided by GitLab
client_secret: .... # Replace this with the OAuth2 Client Secret provided by GitLab
mapper_url: file:///etc/config/kratos/oidc.gitlab.jsonnet
scope:
- read_user
- openid
- profile
- email
```

GitLab is now an option to log in via Kratos.

## Microsoft

This will enable you to log in using any Azure AD directory - Multitenant and
Expand Down
1 change: 1 addition & 0 deletions docs/docs/self-service/flows/user-login.mdx
Expand Up @@ -193,6 +193,7 @@ The Social Sign In Method (`oidc`) enables you to use

- [GitHub](http://github.com/);
- [Apple](https://developer.apple.com/sign-in-with-apple/);
- [GitLab](https://docs.gitlab.com/ee/integration/openid_connect_provider.html);
- [Google](https://developers.google.com/identity/sign-in/web/sign-in);
- [Facebook](https://developers.facebook.com/docs/facebook-login/);
- [ORY Hydra](https://www.ory.sh/hydra);
Expand Down
1 change: 1 addition & 0 deletions docs/docs/self-service/flows/user-registration.mdx
Expand Up @@ -226,6 +226,7 @@ The Social Sign Up Method (`oidc`) enables you to use

- [GitHub](http://github.com/);
- [Apple](https://developer.apple.com/sign-in-with-apple/);
- [GitLab](https://docs.gitlab.com/ee/integration/openid_connect_provider.html);
- [Google](https://developers.google.com/identity/sign-in/web/sign-in);
- [Facebook](https://developers.facebook.com/docs/facebook-login/);
- [ORY Hydra](https://www.ory.sh/hydra);
Expand Down
2 changes: 2 additions & 0 deletions selfservice/strategy/oidc/provider_config.go
Expand Up @@ -83,6 +83,8 @@ func (c ConfigurationCollection) Provider(id string, public *url.URL) (Provider,
return NewProviderGoogle(&p, public), nil
case "github":
return NewProviderGitHub(&p, public), nil
case "gitlab":
return NewProviderGitLab(&p, public), nil
case "microsoft":
return NewProviderMicrosoft(&p, public), nil
}
Expand Down
73 changes: 73 additions & 0 deletions selfservice/strategy/oidc/provider_gitlab.go
@@ -0,0 +1,73 @@
package oidc

import (
"context"
"encoding/json"
"net/http"
"net/url"
"path"

"github.com/ory/herodot"
"github.com/pkg/errors"
"golang.org/x/oauth2"
)

const (
defaultEndpoint = "https://gitlab.com"
)

type ProviderGitLab struct {
*ProviderGenericOIDC
}

func NewProviderGitLab(
config *Configuration,
public *url.URL,
) *ProviderGitLab {
return &ProviderGitLab{
ProviderGenericOIDC: &ProviderGenericOIDC{
config: config,
public: public,
},
}
}

func (g *ProviderGitLab) Claims(ctx context.Context, exchange *oauth2.Token) (*Claims, error) {
o, err := g.OAuth2(ctx)
if err != nil {
return nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf("%s", err))
}

client := o.Client(ctx, exchange)

u, err := g.endpoint()
if err != nil {
return nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf("%s", err))
}
u.Path = path.Join(u.Path, "/oauth/userinfo")
req, err := http.NewRequest("GET", u.String(), nil)
if err != nil {
return nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf("%s", err))
}

resp, err := client.Do(req)
if err != nil {
return nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf("%s", err))
}
defer resp.Body.Close()

var claims Claims
if err := json.NewDecoder(resp.Body).Decode(&claims); err != nil {
return nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf("%s", err))
}

return &claims, nil
}

func (g *ProviderGitLab) endpoint() (*url.URL, error) {
var e = defaultEndpoint
if len(g.config.IssuerURL) > 0 {
e = g.config.IssuerURL
}
return url.Parse(e)
}

0 comments on commit 8580d96

Please sign in to comment.