Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion embedx/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,8 @@
"dingtalk",
"patreon",
"linkedin",
"lark"
"lark",
"epic-games"
],
"examples": ["google"]
},
Expand Down
2 changes: 2 additions & 0 deletions selfservice/strategy/oidc/provider_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type Configuration struct {
// - dingtalk
// - linkedin
// - patreon
// - epic-games
Provider string `json:"provider"`

// Label represents an optional label which can be used in the UI generation.
Expand Down Expand Up @@ -154,6 +155,7 @@ var supportedProviders = map[string]func(config *Configuration, reg Dependencies
"linkedin": NewProviderLinkedIn,
"patreon": NewProviderPatreon,
"lark": NewProviderLark,
"epic-games": NewProviderEpicGames,
}

func (c ConfigurationCollection) Provider(id string, reg Dependencies) (Provider, error) {
Expand Down
118 changes: 118 additions & 0 deletions selfservice/strategy/oidc/provider_epic_games.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0

package oidc

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

"github.com/hashicorp/go-retryablehttp"

"github.com/ory/x/httpx"

"github.com/pkg/errors"
"golang.org/x/oauth2"

"github.com/ory/herodot"
)

type ProviderEpicGames struct {
config *Configuration
reg Dependencies
}

type EpicGamesIdentityResponse struct {
Data struct {
Attributes struct {
Email string `json:"email"`
FirstName string `json:"first_name"`
FullName string `json:"full_name"`
ImageUrl string `json:"image_url"`
LastName string `json:"last_name"`
} `json:"attributes"`
Id string `json:"id"`
Type string `json:"type"`
} `json:"data"`
}

func NewProviderEpicGames(
config *Configuration,
reg Dependencies,
) Provider {
return &ProviderEpicGames{
config: config,
reg: reg,
}
}

func (e *ProviderEpicGames) Config() *Configuration {
return e.config
}

func (e *ProviderEpicGames) oauth2(ctx context.Context) *oauth2.Config {
return &oauth2.Config{
ClientID: e.config.ClientID,
ClientSecret: e.config.ClientSecret,
Endpoint: oauth2.Endpoint{
AuthURL: "https://www.epicgames.com/id/authorize",
TokenURL: "https://api.epicgames.dev/epic/oauth/v1/token",
AuthStyle: oauth2.AuthStyleInHeader,
},
RedirectURL: e.config.Redir(e.reg.Config().OIDCRedirectURIBase(ctx)),
Scopes: e.config.Scope,
}
}

func (e *ProviderEpicGames) OAuth2(ctx context.Context) (*oauth2.Config, error) {
e.reg.Logger().WithField("provider", "epic-games").Trace("ProviderCreating new oauth2 configuration in OAuth2 method.")
return e.oauth2(ctx), nil
}

func (e *ProviderEpicGames) Claims(ctx context.Context, exchange *oauth2.Token, query url.Values) (*Claims, error) {
identityUrl := "https://api.epicgames.dev/epic/oauth/v1/userInfo"

o := e.oauth2(ctx)
ctx, client := httpx.SetOAuth2(ctx, e.reg.HTTPClient(ctx), o, exchange)

req, err := retryablehttp.NewRequest("GET", identityUrl, nil)
if err != nil {
return nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf("%s", err))
}
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Authorization", "Bearer "+exchange.AccessToken)

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

if err := logUpstreamError(e.reg.Logger(), res); err != nil {
return nil, err
}

data := EpicGamesIdentityResponse{}
jsonErr := json.NewDecoder(res.Body).Decode(&data)
if jsonErr != nil {
return nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf("%s", jsonErr))
}

claims := &Claims{
Issuer: "https://api.epicgames.dev/epic/oauth/v1",
Subject: data.Data.Id,
Name: data.Data.Attributes.FullName,
Email: data.Data.Attributes.Email,
GivenName: data.Data.Attributes.FirstName,
FamilyName: data.Data.Attributes.LastName,
LastName: data.Data.Attributes.LastName,
Picture: data.Data.Attributes.ImageUrl,
}

return claims, nil
}

func (e *ProviderEpicGames) AuthCodeURLOptions(r ider) []oauth2.AuthCodeOption {
return []oauth2.AuthCodeOption{}
}
1 change: 1 addition & 0 deletions selfservice/strategy/oidc/provider_private_net_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ func TestProviderPrivateIP(t *testing.T) {
// VK uses a fixed token URL and does not use the issuer.
// Yandex uses a fixed token URL and does not use the issuer.
// NetID uses a fixed token URL and does not use the issuer.
// Epic Games uses a fixed token URL and does not use the issuer.
} {
t.Run(fmt.Sprintf("case=%d", k), func(t *testing.T) {
p := tc.p(tc.c)
Expand Down
3 changes: 2 additions & 1 deletion test/e2e/cypress/support/config.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ export type SelfServiceOIDCProvider1 = {
[k: string]: unknown | undefined
}
/**
* Can be one of github, github-app, gitlab, generic, google, microsoft, discord, slack, facebook, auth0, vk, yandex, apple, spotify, netid, dingtalk, patreon.
* Can be one of github, github-app, gitlab, generic, google, microsoft, discord, slack, facebook, auth0, vk, yandex, apple, spotify, netid, dingtalk, patreon, epic-games.
*/
export type Provider =
| "github"
Expand All @@ -229,6 +229,7 @@ export type Provider =
| "patreon"
| "linkedin"
| "lark"
| "epic-games"
export type OptionalStringWhichWillBeUsedWhenGeneratingLabelsForUIButtons =
string
/**
Expand Down