Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OIDC #24

Open
wants to merge 6 commits into
base: dev
Choose a base branch
from
Open

OIDC #24

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: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,6 @@ config.yaml

# Exclude air temp directory
target/

#Exclude values.yaml
/config/values.yaml
3 changes: 2 additions & 1 deletion api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ func Start() {

r.GET("/recovery", HandleGetRecoveryFlow)
r.POST("/recovery", HandlePostRecoveryFlow)

r.POST("/login/oidc/:provider", HandleOIDCLogin)
r.POST("/register/oidc/:provider", HandleOIDCRegister)
r.Run()
// listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
104 changes: 104 additions & 0 deletions api/oidc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package api

import (
"net/http"

"github.com/gin-gonic/gin"
"github.com/sdslabs/nymeria/log"
"github.com/sdslabs/nymeria/pkg/wrapper/kratos/login"
"github.com/sdslabs/nymeria/pkg/wrapper/kratos/oidc"
"github.com/sdslabs/nymeria/pkg/wrapper/kratos/registration"
)

func HandleOIDCLogin(c *gin.Context) {
log.Logger.Debug("Get OIDC Login")
provider := c.Param("provider")
if provider == "" {
c.JSON(http.StatusBadRequest, gin.H{
"error": "provider not found",
})
return
}
cookie, flowID, csrf_token, err := login.InitializeLoginFlowWrapper()

if err != nil {
log.ErrorLogger("Intialize OIDC Login Failed", err)
c.JSON(http.StatusInternalServerError, gin.H{
"error": "internal server error",
})
return
}
c.SetCookie("OIDC_login_flow", cookie, 3600, "/", "localhost", false, true)
//In case we need to separate the flows so setting and getting cookies simultaneously
afterCookie, err := c.Cookie("OIDC_login_flow")

if err != nil {
log.ErrorLogger("Cookie not found", err)
c.JSON(http.StatusBadRequest, gin.H{
"error": "csrf cookie not found",
})
return
}

session, err := oidc.SubmitOIDCLoginFlowWrapper(provider, afterCookie, flowID, csrf_token)

if err != nil {
log.ErrorLogger("Kratos post OIDC login flow failed", err)
c.JSON(http.StatusInternalServerError, gin.H{
"error": "internal server error",
})
return
}

c.SetCookie("sdslabs_session", session, 3600, "/", "localhost", false, true)
c.JSON(http.StatusOK, gin.H{
"status": "user logged in via OIDC",
})

}

func HandleOIDCRegister(c *gin.Context) {
log.Logger.Debug("Get OIDC Registration")
provider := c.Param("provider")
if provider == "" {
c.JSON(http.StatusBadRequest, gin.H{
"error": "provider not found",
})
return
}
cookie, flowID, csrf_token, err := registration.InitializeRegistrationFlowWrapper()

if err != nil {
log.ErrorLogger("Kratos get OIDC registration flow failed", err)
c.JSON(http.StatusInternalServerError, gin.H{
"error": "internal server error",
})
return
}

c.SetCookie("OIDC_registration_flow", cookie, 3600, "/", "localhost", false, true)
//In case we need to separate the flows so setting and getting cookies simultaneously
afterCookie, err := c.Cookie("OIDC_registration_flow")

if err != nil {
log.ErrorLogger("Cookie not found", err)
c.JSON(http.StatusBadRequest, gin.H{
"error": "csrf cookie not found",
})
return
}
session, err := oidc.SubmitOIDCRegistrationFlowWrapper(provider, afterCookie, flowID, csrf_token)

if err != nil {
log.ErrorLogger("Kratos OIDC post registration flow failed", err)
c.JSON(http.StatusInternalServerError, gin.H{
"error": "internal server error",
})
return
}
c.SetCookie("sdslabs_session", session, 3600, "/", "localhost", false, true)
c.JSON(http.StatusOK, gin.H{
"status": "created via OIDC",
})

}
25 changes: 25 additions & 0 deletions config/kratos_config.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
selfservice:
strategies:
oidc:
config:
providers:
- id: google # this is `<provider-id>` in the Authorization callback URL. DO NOT CHANGE IT ONCE SET!
provider: google
client_id: {{google.client_id}} # Replace this with the OAuth2 Client ID
client_secret: {{google.client_secret}} # Replace this with the OAuth2 Client secret
mapper_url: file:///etc/config/kratos/oidc.google.jsonnet
# Alternatively, use an URL:
# mapper_url: https://storage.googleapis.com/abc-cde-prd/9cac9717f007808bf17f22ce7f4295c739604b183f05ac4afb4
scope:
- email
- profile
# other supported scopes can be found in Google OAuth 2.0 dev docs
requested_claims:
id_token:
email:
essential: true
email_verified:
essential: true
given_name:
essential: true
family_name: null
hd: null # If you want the Google Workspace domain
enabled: true
password:
enabled: true

Expand Down
11 changes: 11 additions & 0 deletions config/oidc.google.jsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
local claims = {
email_verified: true,
} + std.extVar('claims');

{
identity: {
traits: {
[if 'email' in claims && claims.email_verified then 'email' else null]: claims.email,
},
},
}
1 change: 0 additions & 1 deletion pkg/controller/admin/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"net/http"
"os"
"strconv"
"strings"

"github.com/gin-gonic/gin"
client "github.com/ory/client-go"
Expand Down
44 changes: 44 additions & 0 deletions pkg/wrapper/kratos/oidc/oidc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package oidc

import (
"context"
"fmt"
"os"

client "github.com/ory/client-go"
"github.com/sdslabs/nymeria/config"
)

func SubmitOIDCRegistrationFlowWrapper(provider string, cookie string, flowID string, csrfToken string) (string, error) {
submitOIDCRegistrationFlowBody := client.SubmitSelfServiceRegistrationFlowBody{
SubmitSelfServiceRegistrationFlowWithOidcMethodBody: client.NewSubmitSelfServiceRegistrationFlowWithOidcMethodBody("oidc", provider),
}

submitOIDCRegistrationFlowBody.SubmitSelfServiceRegistrationFlowWithOidcMethodBody.SetCsrfToken(csrfToken)

apiClient := client.NewAPIClient(config.KratosClientConfig)
_, r, err := apiClient.V0alpha2Api.SubmitSelfServiceRegistrationFlow(context.Background()).Flow(flowID).SubmitSelfServiceRegistrationFlowBody(submitOIDCRegistrationFlowBody).Cookie(cookie).Execute()
if err != nil {
fmt.Fprintf(os.Stderr, "Error when calling `V0alpha2Api.SubmitSelfServiceRegistrationFlow``: %v\n", err)
fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r)
}

responseCookies := r.Header["Set-Cookie"]
return responseCookies[1], nil
}

func SubmitOIDCLoginFlowWrapper(provider string, cookie string, flowID string, csrfToken string) (string, error) {
submitOIDCLoginFlowBody := client.SubmitSelfServiceLoginFlowBody{SubmitSelfServiceLoginFlowWithOidcMethodBody: client.NewSubmitSelfServiceLoginFlowWithOidcMethodBody("oidc", provider)} // SubmitSelfServiceLoginFlowBody |

submitOIDCLoginFlowBody.SubmitSelfServiceLoginFlowWithOidcMethodBody.SetCsrfToken(csrfToken)

apiClient := client.NewAPIClient(config.KratosClientConfig)
_, r, err := apiClient.V0alpha2Api.SubmitSelfServiceLoginFlow(context.Background()).Flow(flowID).SubmitSelfServiceLoginFlowBody(submitOIDCLoginFlowBody).XSessionToken("").Cookie(cookie).Execute()
if err != nil {
return "", err
}

responseCookies := r.Header["Set-Cookie"]

return responseCookies[1], nil
}