Skip to content

Commit

Permalink
Merge pull request #1350 from mokrinsky/authentik-simlplified-flow
Browse files Browse the repository at this point in the history
Support the new flow used in Authentik
  • Loading branch information
mapkon authored Sep 30, 2024
2 parents 9b3c6ec + 3d2422d commit b356b40
Show file tree
Hide file tree
Showing 3 changed files with 223 additions and 2 deletions.
5 changes: 3 additions & 2 deletions pkg/provider/authentik/authentik.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,12 @@ func (kc *Client) queryNext(ctx *authentikContext) (bool, string, error) {
if err != nil {
return false, "", err
}
if payload.isTypeRedirect() {

if payload.isTypeRedirect() || payload.isComponentFlowRedirect() {
// login success if there is a redirect
logger.Debug("Login success, redirect to saml response")
return false, payload.RedirectTo, nil
} else if !payload.isTypeNative() {
} else if !payload.isTypeNative() && !payload.isTypeEmpty() {
return false, "", errors.New("Unknown type: " + payload.Type)
}

Expand Down
212 changes: 212 additions & 0 deletions pkg/provider/authentik/authentik_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,3 +318,215 @@ func Test_authWithCombinedUsernamePassword(t *testing.T) {
assert.Nil(err)
assert.Equal(result, samlResponse)
}

// Test_simplifiedFlowAuthWithSeperatedUsernamePassword Password only if username/email verified
func Test_simplifiedFlowAuthWithSeperatedUsernamePassword(t *testing.T) {
defer gock.Off()
samlResponse := "PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaX"
gock.New("http://127.0.0.1").
Get("/application/saml/aws/sso/binding/init").
Reply(302).
SetHeader("Set-Cookie", "[authentik_session=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzaWQiOiJ6cHI3NGdzMjNnOGNqbmF1bXNheGQ1dXVrc2VtZGZpNyIsImlzcyI6ImF1dGhlbnRpayIsInN1YiI6ImFub255bW91cyIsImF1dGhlbnRpY2F0ZWQiOmZhbHNlLCJhY3IiOiJnb2F1dGhlbnRpay5pby9jb3JlL2RlZmF1bHQifQ.zNiX4pk6G9ABeDip0PLs8-0irm2aQ_Arr_RgTxTGCQM; HttpOnly; Path=/; SameSite=None; Secure]").
SetHeader("Location", "/flows/-/default/authentication/?next=/application/saml/aws/sso/binding/init/")

gock.New("http://127.0.0.1").
Get("/flows/-/default/authentication").
Reply(302).
SetHeader("Location", "/if/flow/default-authentication-flow/?next=%2Fapplication%2Fsaml%2Faws%2Fsso%2Fbinding%2Finit%2F")

gock.New("http://127.0.0.1").
Get("/if/flow/default-authentication-flow").
Reply(200).
BodyString("")

gock.New("http://127.0.0.1").
Get("/api/v3/flows/executor/default-authentication-flow").
Reply(200).
JSON(map[string]interface{}{
"flow_info": map[string]interface{}{"title": "Welcome to authentik!", "background": "/static/dist/assets/images/flow_background.jpg", "cancel_url": "/flows/-/cancel/", "layout": "stacked"},
"component": "ak-stage-identification",
"user_fields": []string{"username", "email"},
"password_fields": false,
"application_pre": "aws",
"primary_action": "Log in",
"sources": []string{},
"show_source_labels": false,
})

gock.New("http://127.0.0.1").
Post("/api/v3/flows/executor/default-authentication-flow").
Reply(302).
SetHeader("Location", "/api/v3/flows/executor/default-authentication-flow/?query=next%3D%252F")

gock.New("http://127.0.0.1").
Get("api/v3/flows/executor/default-authentication-flow").
Reply(200).
JSON(map[string]interface{}{
"flow_info": map[string]interface{}{"title": "Welcome to authentik!", "background": "/static/dist/assets/images/flow_background.jpg", "cancel_url": "/flows/-/cancel/", "layout": "stacked"},
"component": "ak-stage-password",
"pending_user": "user",
"pending_user_avatar": "https://secure.gravatar.com/avatar/0932141298741243?s=158&r=g",
})
gock.New("http://127.0.0.1").
Post("/api/v3/flows/executor/default-authentication-flow").
Reply(302).
SetHeader("Location", "/api/v3/flows/executor/default-authentication-flow/?query=next%3D%252F")

gock.New("http://127.0.0.1").
Get("/api/v3/flows/executor/default-authentication-flow").
Reply(302).
SetHeader("Location", "/api/v3/flows/executor/default-authentication-flow/?query=next%3D%252F")

gock.New("http://127.0.0.1").
Get("/api/v3/flows/executor/default-authentication-flow").
Reply(200).
JSON(map[string]interface{}{
"component": "xak-flow-redirect",
"to": "http://127.0.0.1/application/saml/aws/sso/binding/init",
})

gock.New("http://127.0.0.1").
Get("/application/saml/aws/sso/binding/init").
Reply(302).
SetHeader("Location", "/if/flow/default-provider-authorization-implicit-consent/")

gock.New("http://127.0.0.1").
Get("/if/flow/default-provider-authorization-implicit-consent/").
Reply(200)

gock.New("http://127.0.0.1").
Get("/api/v3/flows").
Reply(200).
JSON(map[string]interface{}{
"flow_info": map[string]interface{}{
"title": "Redirecting to aws",
"background": "/static/dist/assets/images/flow_background.jpg",
"cancel_url": "/flows/-/cancel/",
"layout": "stacked",
},
"component": "ak-stage-autosubmit",
"url": "https://signin.amazonaws.com/saml",
"attrs": map[string]interface{}{
"ACSUrl": "https://signin.amazonaws.com/saml",
"SAMLResponse": samlResponse,
},
})
client, _ := New(&cfg.IDPAccount{})
loginDetails := &creds.LoginDetails{
Username: "user",
Password: "pwd",
URL: "http://127.0.0.1/application/saml/aws/sso/binding/init",
}
gock.InterceptClient(&client.client.Client)
result, err := client.Authenticate(loginDetails)

assert := assert.New(t)
assert.Nil(err)
assert.Equal(result, samlResponse)
}

// Test_simplifiedFlowAuthWithCombinedUsernamePassword Username/email and password in one page
func Test_simplifiedFlowAuthWithCombinedUsernamePassword(t *testing.T) {
defer gock.Off()
samlResponse := "PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaX"
gock.New("http://127.0.0.1").
Get("/application/saml/aws/sso/binding/init").
Reply(302).
SetHeader("Set-Cookie", "[authentik_session=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzaWQiOiJ6cHI3NGdzMjNnOGNqbmF1bXNheGQ1dXVrc2VtZGZpNyIsImlzcyI6ImF1dGhlbnRpayIsInN1YiI6ImFub255bW91cyIsImF1dGhlbnRpY2F0ZWQiOmZhbHNlLCJhY3IiOiJnb2F1dGhlbnRpay5pby9jb3JlL2RlZmF1bHQifQ.zNiX4pk6G9ABeDip0PLs8-0irm2aQ_Arr_RgTxTGCQM; HttpOnly; Path=/; SameSite=None; Secure]").
SetHeader("Location", "/flows/-/default/authentication/?next=/application/saml/aws/sso/binding/init/")

gock.New("http://127.0.0.1").
Get("/flows/-/default/authentication").
Reply(302).
SetHeader("Location", "/if/flow/default-authentication-flow/?next=%2Fapplication%2Fsaml%2Faws%2Fsso%2Fbinding%2Finit%2F")

gock.New("http://127.0.0.1").
Get("/if/flow/default-authentication-flow").
Reply(200).
BodyString("")

gock.New("http://127.0.0.1").
Get("/api/v3/flows/executor/default-authentication-flow").
Reply(200).
JSON(map[string]interface{}{
"flow_info": map[string]interface{}{"title": "Welcome to authentik!", "background": "/static/dist/assets/images/flow_background.jpg", "cancel_url": "/flows/-/cancel/", "layout": "stacked"},
"component": "ak-stage-identification",
"user_fields": []string{"username", "email"},
"password_fields": true,
"application_pre": "aws",
"primary_action": "Log in",
"sources": []string{},
"show_source_labels": false,
})

gock.New("http://127.0.0.1").
Post("/api/v3/flows/executor/default-authentication-flow").
Reply(302).
SetHeader("Location", "/api/v3/flows/executor/default-authentication-flow/?query=next%3D%252F")

gock.New("http://127.0.0.1").
Get("api/v3/flows/executor/default-authentication-flow").
Reply(200).
JSON(map[string]interface{}{
"flow_info": map[string]interface{}{"title": "Welcome to authentik!", "background": "/static/dist/assets/images/flow_background.jpg", "cancel_url": "/flows/-/cancel/", "layout": "stacked"},
"component": "ak-stage-password",
"pending_user": "user",
"pending_user_avatar": "https://secure.gravatar.com/avatar/0932141298741243?s=158&r=g",
})
gock.New("http://127.0.0.1").
Post("/api/v3/flows/executor/default-authentication-flow").
Reply(302).
SetHeader("Location", "/api/v3/flows/executor/default-authentication-flow/?query=next%3D%252F")

gock.New("http://127.0.0.1").
Get("/api/v3/flows/executor/default-authentication-flow").
Reply(302).
SetHeader("Location", "/api/v3/flows/executor/default-authentication-flow/?query=next%3D%252F")

gock.New("http://127.0.0.1").
Get("/api/v3/flows/executor/default-authentication-flow").
Reply(200).
JSON(map[string]interface{}{
"component": "xak-flow-redirect",
"to": "http://127.0.0.1/application/saml/aws/sso/binding/init",
})

gock.New("http://127.0.0.1").
Get("/application/saml/aws/sso/binding/init").
Reply(302).
SetHeader("Location", "/if/flow/default-provider-authorization-implicit-consent/")

gock.New("http://127.0.0.1").
Get("/if/flow/default-provider-authorization-implicit-consent/").
Reply(200)

gock.New("http://127.0.0.1").
Get("/api/v3/flows").
Reply(200).
JSON(map[string]interface{}{
"flow_info": map[string]interface{}{
"title": "Redirecting to aws",
"background": "/static/dist/assets/images/flow_background.jpg",
"cancel_url": "/flows/-/cancel/",
"layout": "stacked",
},
"component": "ak-stage-autosubmit",
"url": "https://signin.amazonaws.com/saml",
"attrs": map[string]interface{}{
"ACSUrl": "https://signin.amazonaws.com/saml",
"SAMLResponse": samlResponse,
},
})
client, _ := New(&cfg.IDPAccount{})
loginDetails := &creds.LoginDetails{
Username: "user",
Password: "pwd",
URL: "http://127.0.0.1/application/saml/aws/sso/binding/init",
}
gock.InterceptClient(&client.client.Client)
result, err := client.Authenticate(loginDetails)

assert := assert.New(t)
assert.Nil(err)
assert.Equal(result, samlResponse)
}
8 changes: 8 additions & 0 deletions pkg/provider/authentik/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ func (payload *authentikPayload) isTypeRedirect() bool {
return payload.Type == "redirect"
}

func (payload *authentikPayload) isTypeEmpty() bool {
return payload.Type == ""
}

func (payload *authentikPayload) isComponentStageAutosubmit() bool {
return payload.Component == "ak-stage-autosubmit"
}

func (payload *authentikPayload) isComponentFlowRedirect() bool {
return payload.Component == "xak-flow-redirect"
}

0 comments on commit b356b40

Please sign in to comment.