package providers import ( "context" "encoding/json" "errors" "fmt" "net/http" "net/http/cookiejar" ory "github.com/ory/kratos-client-go" "github.com/up9inc/mizu/shared/logger" ) var client = getKratosClient("http://127.0.0.1:4433", "http://127.0.0.1:4434") // returns session token if successful func RegisterUser(username string, password string, ctx context.Context) (token *string, identityId string, err error, formErrorMessages map[string][]ory.UiText) { flow, _, err := client.V0alpha2Api.InitializeSelfServiceRegistrationFlowWithoutBrowser(ctx).Execute() if err != nil { return nil, "", err, nil } result, _, err := client.V0alpha2Api.SubmitSelfServiceRegistrationFlow(ctx).Flow(flow.Id).SubmitSelfServiceRegistrationFlowBody( ory.SubmitSelfServiceRegistrationFlowWithPasswordMethodBodyAsSubmitSelfServiceRegistrationFlowBody(&ory.SubmitSelfServiceRegistrationFlowWithPasswordMethodBody{ Method: "password", Password: password, Traits: map[string]interface{}{"username": username}, }), ).Execute() if err != nil { parsedKratosError, parsingErr := parseKratosRegistrationFormError(err) if parsingErr != nil { logger.Log.Debugf("error parsing kratos error: %v", parsingErr) return nil, "", err, nil } else { return nil, "", err, parsedKratosError } } return result.SessionToken, result.Identity.Id, nil, nil } func PerformLogin(username string, password string, ctx context.Context) (*string, error) { flow, _, err := client.V0alpha2Api.InitializeSelfServiceLoginFlowWithoutBrowser(ctx).Execute() if err != nil { return nil, err } result, _, err := client.V0alpha2Api.SubmitSelfServiceLoginFlow(ctx).Flow(flow.Id).SubmitSelfServiceLoginFlowBody( ory.SubmitSelfServiceLoginFlowWithPasswordMethodBodyAsSubmitSelfServiceLoginFlowBody(&ory.SubmitSelfServiceLoginFlowWithPasswordMethodBody{ Method: "password", Password: password, PasswordIdentifier: username, }), ).Execute() if err != nil { return nil, err } if result == nil { return nil, errors.New("unknown error occured during login") } return result.SessionToken, nil } func VerifyToken(token string, ctx context.Context) (bool, error) { flow, _, err := client.V0alpha2Api.ToSession(ctx).XSessionToken(token).Execute() if err != nil { return false, err } if flow == nil { return false, nil } return true, nil } func DeleteUser(identityId string, ctx context.Context) error { result, err := client.V0alpha2Api.AdminDeleteIdentity(ctx, identityId).Execute() if err != nil { return err } if result == nil { return errors.New("unknown error occured during user deletion") } if result.StatusCode < 200 || result.StatusCode > 299 { return errors.New(fmt.Sprintf("user deletion returned bad status %d", result.StatusCode)) } else { return nil } } func AnyUserExists(ctx context.Context) (bool, error) { request := client.V0alpha2Api.AdminListIdentities(ctx) request.PerPage(1) if result, _, err := request.Execute(); err != nil { return false, err } else { return len(result) > 0, nil } } func Logout(token string, ctx context.Context) error { logoutRequest := client.V0alpha2Api.SubmitSelfServiceLogoutFlowWithoutBrowser(ctx) logoutRequest = logoutRequest.SubmitSelfServiceLogoutFlowWithoutBrowserBody(ory.SubmitSelfServiceLogoutFlowWithoutBrowserBody{ SessionToken: token, }) if response, err := logoutRequest.Execute(); err != nil { return err } else if response == nil || response.StatusCode < 200 || response.StatusCode > 299 { return errors.New("unknown error occured during logout") } return nil } func getKratosClient(url string, adminUrl string) *ory.APIClient { conf := ory.NewConfiguration() conf.Servers = ory.ServerConfigurations{{URL: url}} // this ensures kratos client uses the admin url for admin actions (any new admin action we use will have to be added here) conf.OperationServers = map[string]ory.ServerConfigurations{ "V0alpha2ApiService.AdminDeleteIdentity": {{URL: adminUrl}}, "V0alpha2ApiService.AdminListIdentities": {{URL: adminUrl}}, } cj, _ := cookiejar.New(nil) conf.HTTPClient = &http.Client{Jar: cj} return ory.NewAPIClient(conf) } // returns map of form value key to error message func parseKratosRegistrationFormError(err error) (map[string][]ory.UiText, error) { var openApiError *ory.GenericOpenAPIError if errors.As(err, &openApiError) { var registrationFlowModel *ory.SelfServiceRegistrationFlow if jsonErr := json.Unmarshal(openApiError.Body(), ®istrationFlowModel); jsonErr != nil { return nil, jsonErr } else { formMessages := registrationFlowModel.Ui.Nodes parsedMessages := make(map[string][]ory.UiText) for _, message := range formMessages { if len(message.Messages) > 0 { if _, ok := parsedMessages[message.Group]; !ok { parsedMessages[message.Group] = make([]ory.UiText, 0) } parsedMessages[message.Group] = append(parsedMessages[message.Group], message.Messages...) } } return parsedMessages, nil } } else { return nil, errors.New("error is not a generic openapi error") } }