From 6779c3afd1a47356d18abd04e0353fbe29ee75a3 Mon Sep 17 00:00:00 2001 From: Rajat Bajaj Date: Thu, 5 Dec 2024 14:39:43 +0530 Subject: [PATCH] v1 --- authentication/authentication.go | 2 + authentication/authentication_test.go | 14 ++-- authentication/ciba.go | 52 ++++++++++++++ authentication/ciba/ciba.go | 22 ++++++ authentication/ciba_test.go | 72 +++++++++++++++++++ authentication/oauth_test.go | 8 +-- .../authentication/TestCIBA_Initiate.yaml | 46 ++++++++++++ 7 files changed, 206 insertions(+), 10 deletions(-) create mode 100644 authentication/ciba.go create mode 100644 authentication/ciba/ciba.go create mode 100644 authentication/ciba_test.go create mode 100644 test/data/recordings/authentication/TestCIBA_Initiate.yaml diff --git a/authentication/authentication.go b/authentication/authentication.go index 97087684..c1dc0ba5 100644 --- a/authentication/authentication.go +++ b/authentication/authentication.go @@ -123,6 +123,7 @@ type Authentication struct { MFA *MFA OAuth *OAuth Passwordless *Passwordless + CIBA *CIBA auth0ClientInfo *client.Auth0ClientInfo basePath string @@ -184,6 +185,7 @@ func New(ctx context.Context, domain string, options ...Option) (*Authentication a.MFA = (*MFA)(&a.common) a.OAuth = (*OAuth)(&a.common) a.Passwordless = (*Passwordless)(&a.common) + a.CIBA = (*CIBA)(&a.common) validatorOpts := []idtokenvalidator.Option{} diff --git a/authentication/authentication_test.go b/authentication/authentication_test.go index f19e1134..63f654f7 100644 --- a/authentication/authentication_test.go +++ b/authentication/authentication_test.go @@ -527,7 +527,7 @@ func usingRecordingResponses(t *testing.T) bool { return httpRecordingsEnabled && domain == "go-auth0-dev.eu.auth0.com" } -func givenAUser(t *testing.T) userDetails { +func givenAUser(t *testing.T) *management.User { t.Helper() if !usingRecordingResponses(t) { @@ -547,12 +547,14 @@ func givenAUser(t *testing.T) userDetails { err := mgmtAPI.User.Delete(context.Background(), user.GetID()) require.NoError(t, err) }) + + return user } - return userDetails{ - connection: "Username-Password-Authentication", - email: "chuck@example.com", - password: "Testpassword123!", - username: "test-user", + return &management.User{ + Connection: auth0.String("Username-Password-Authentication"), + Email: auth0.String("chuck@example.com"), + Password: auth0.String("Testpassword123!"), + Username: auth0.String("test-user"), } } diff --git a/authentication/ciba.go b/authentication/ciba.go new file mode 100644 index 00000000..f785c9f9 --- /dev/null +++ b/authentication/ciba.go @@ -0,0 +1,52 @@ +package authentication + +import ( + "context" + "fmt" + "github.com/auth0/go-auth0/authentication/ciba" + "net/url" + "strings" +) + +// CIBA manager. +type CIBA manager + +// Initiate given a CIBA request params initiates a CIBA authentication request. +// +// This sends a POST request to the /bc-authorize endpoint and returns an auth_req_id for polling. +func (c *CIBA) Initiate(ctx context.Context, body ciba.Request, opts ...RequestOption) (r *ciba.Response, err error) { + if body.ClientID == "" { + body.ClientID = c.authentication.clientID + } + + if body.ClientSecret == "" { + body.ClientSecret = c.authentication.clientSecret + } + + var missing []string + check(&missing, "ClientID", body.ClientID != "" || c.authentication.clientID != "") + check(&missing, "ClientSecret", body.ClientSecret != "" || c.authentication.clientSecret != "") + check(&missing, "LoginHint", body.LoginHint != "") + check(&missing, "Scope", body.Scope != "") + check(&missing, "BindingMessage", body.BindingMessage != "") + + if len(missing) > 0 { + return nil, fmt.Errorf("missing required fields: %s", strings.Join(missing, ", ")) + } + + data := url.Values{ + "client_id": []string{body.ClientID}, + "client_secret": []string{body.ClientSecret}, + "login_hint": []string{body.LoginHint}, + "scope": []string{body.Scope}, + "binding_message": []string{body.BindingMessage}, + } + + // Perform the request + err = c.authentication.Request(ctx, "POST", c.authentication.URI("bc-authorize"), data, &r, opts...) + if err != nil { + return nil, err + } + + return +} diff --git a/authentication/ciba/ciba.go b/authentication/ciba/ciba.go new file mode 100644 index 00000000..f52860be --- /dev/null +++ b/authentication/ciba/ciba.go @@ -0,0 +1,22 @@ +package ciba + +// Request defines the request body for calling the bc-authorize endpoint +type Request struct { + //ClientAuthentication + // The client_id of your client. + ClientID string `json:"client_id,omitempty"` + // The client_secret of your client. + ClientSecret string `json:"client_secret,omitempty"` + + LoginHint string `json:"login_hint,omitempty"` + Scope string `json:"scope,omitempty"` + Audience string `json:"audience,omitempty"` + BindingMessage string `json:"binding_message,omitempty"` +} + +// Response defines the response of the CIBA request +type Response struct { + AuthReqID string `json:"auth_req_id,omitempty"` + ExpiresIn int64 `json:"expires_in,omitempty"` + Interval int64 `json:"interval,omitempty"` +} diff --git a/authentication/ciba_test.go b/authentication/ciba_test.go new file mode 100644 index 00000000..f398afc7 --- /dev/null +++ b/authentication/ciba_test.go @@ -0,0 +1,72 @@ +package authentication + +import ( + "context" + "fmt" + "github.com/auth0/go-auth0/authentication/ciba" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "testing" +) + +func TestCIBA_Initiate(t *testing.T) { + configureHTTPTestRecordings(t, authAPI) + + user := givenAUser(t) + + // Call the Initiate method of the CIBA manager + resp, err := authAPI.CIBA.Initiate(context.Background(), ciba.Request{ + ClientID: mgmtClientID, + ClientSecret: mgmtClientSecret, + Scope: "openid", + LoginHint: fmt.Sprintf(`{"format":"iss_sub","iss":"https://witty-silver-sailfish-sus1-staging-20240704.sus.auth0.com/","sub":"%s"}`, user.GetID()), + BindingMessage: "TEST-BINDING-MESSAGE", + }) + + // Validate the response + require.NoError(t, err) + require.NotNil(t, resp) + assert.NotEmpty(t, resp.AuthReqID, "auth_req_id should not be empty") + assert.Greater(t, resp.ExpiresIn, int64(0), "expires_in should be greater than 0") + assert.Greater(t, resp.Interval, int64(0), "interval should be greater than 0") +} + +//func givenAUser(t *testing.T) *management.User { +// t.Helper() +// +// userMetadata := map[string]interface{}{ +// "favourite_attack": "roundhouse_kick", +// } +// appMetadata := map[string]interface{}{ +// "facts": []string{ +// "count_to_infinity_twice", +// "kill_two_stones_with_one_bird", +// "can_hear_sign_language", +// }, +// } +// user := &management.User{ +// Connection: auth0.String("Username-Password-Authentication"), +// Email: auth0.String(fmt.Sprintf("chuck%d@example.com", rand.Intn(999))), +// Password: auth0.String("Passwords hide their chuck"), +// Username: auth0.String(fmt.Sprintf("test-user%d", rand.Intn(999))), +// GivenName: auth0.String("Chuck"), +// FamilyName: auth0.String("Sanchez"), +// Nickname: auth0.String("Chucky"), +// UserMetadata: &userMetadata, +// EmailVerified: auth0.Bool(true), +// VerifyEmail: auth0.Bool(false), +// AppMetadata: &appMetadata, +// Picture: auth0.String("https://example-picture-url.jpg"), +// Blocked: auth0.Bool(false), +// } +// +// err := mgmtAPI.User.Create(context.Background(), user) +// require.NoError(t, err) +// +// t.Cleanup(func() { +// err := mgmtAPI.User.Delete(context.Background(), user.GetID()) +// require.NoError(t, err) +// }) +// +// return user +//} diff --git a/authentication/oauth_test.go b/authentication/oauth_test.go index ef0fa9b6..f5f8f4ab 100644 --- a/authentication/oauth_test.go +++ b/authentication/oauth_test.go @@ -30,8 +30,8 @@ func TestOAuthLoginWithPassword(t *testing.T) { user := givenAUser(t) tokenSet, err := auth.OAuth.LoginWithPassword(context.Background(), oauth.LoginWithPasswordRequest{ - Username: user.username, - Password: user.password, + Username: user.GetUsername(), + Password: user.GetPassword(), }, oauth.IDTokenValidationOptions{}) require.NoError(t, err) assert.NotEmpty(t, tokenSet.AccessToken) @@ -43,8 +43,8 @@ func TestOAuthLoginWithPassword(t *testing.T) { user := givenAUser(t) tokenSet, err := auth.OAuth.LoginWithPassword(context.Background(), oauth.LoginWithPasswordRequest{ - Username: user.username, - Password: user.password, + Username: user.GetUsername(), + Password: user.GetPassword(), Scope: "extra-scope", ExtraParameters: map[string]string{ "extra": "value", diff --git a/test/data/recordings/authentication/TestCIBA_Initiate.yaml b/test/data/recordings/authentication/TestCIBA_Initiate.yaml new file mode 100644 index 00000000..10230761 --- /dev/null +++ b/test/data/recordings/authentication/TestCIBA_Initiate.yaml @@ -0,0 +1,46 @@ +--- +version: 2 +interactions: + - id: 0 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 365 + transfer_encoding: [] + trailer: {} + host: go-auth0-dev.eu.auth0.com + remote_addr: "" + request_uri: "" + body: binding_message=TEST-BINDING-MESSAGE&client_id=test-client_id&client_secret=test-client_secret&login_hint=%7B%22format%22%3A%22iss_sub%22%2C%22iss%22%3A%22https%3A%2F%2Fgo-auth0-dev.eu.auth0.com.sus.auth0.com%2F%22%2C%22sub%22%3A%22auth0%7C6707939cad3d8bec47ecfa2e%22%7D&scope=openid + form: + binding_message: + - TEST-BINDING-MESSAGE + client_id: + - test-client_id + client_secret: + - test-client_secret + login_hint: + - '{"format":"iss_sub","iss":"https://witty-silver-sailfish-sus1-staging-20240704.sus.auth0.com/","sub":"auth0|6707939cad3d8bec47ecfa2e"}' + scope: + - openid + headers: + Content-Type: + - application/x-www-form-urlencoded + url: https://go-auth0-dev.eu.auth0.com/bc-authorize + method: POST + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: -1 + uncompressed: true + body: '{"auth_req_id":"7rL9DmKgR6BdVVwrIGk4PlP-3gdQ60W6Iq8FhhXRybHMXyQVz3r6f3NwSkBaNqqC","expires_in":300,"interval":5}' + headers: + Content-Type: + - application/json; charset=utf-8 + status: 200 OK + code: 200 + duration: 606.440209ms