Skip to content

Commit

Permalink
feat!: enhance SignIn functionality and optimize configuration options (
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaoyijun authored Sep 18, 2024
1 parent 9241a12 commit b96374e
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 30 deletions.
20 changes: 12 additions & 8 deletions client/logto_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ import (
)

type LogtoConfig struct {
Endpoint string
AppId string
AppSecret string
Scopes []string
Resources []string
Prompt string
Endpoint string
AppId string
AppSecret string
Scopes []string
Resources []string
Prompt string
IncludeReservedScopes *bool
}

/**
Expand All @@ -21,8 +22,11 @@ type LogtoConfig struct {
* - Add `ReservedResource.Organization` to resources if `UserScope.Organizations` is included in scopes.
*/
func (logtoConfig *LogtoConfig) normalized() {
for _, defaultScope := range core.DefaultScopes {
logtoConfig.Scopes = core.AppendIfNotExisted(logtoConfig.Scopes, defaultScope)
includeReservedScopes := logtoConfig.IncludeReservedScopes
if includeReservedScopes == nil || *includeReservedScopes {
for _, defaultScope := range core.DefaultScopes {
logtoConfig.Scopes = core.AppendIfNotExisted(logtoConfig.Scopes, defaultScope)
}
}

if slices.Contains(logtoConfig.Scopes, core.UserScopeOrganizations) {
Expand Down
35 changes: 31 additions & 4 deletions client/sign_in.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,24 @@ import (
"github.com/logto-io/go/core"
)

type SignInOptions struct {
RedirectUri string
Prompt string
FirstScreen string
Identifiers []string
DirectSignIn *core.DirectSignInOptions
LoginHint string
ExtraParams map[string]string
}

type SignInSession struct {
RedirectUri string
CodeVerifier string
CodeChallenge string
State string
}

func (logtoClient *LogtoClient) SignIn(redirectUri string) (string, error) {
func (logtoClient *LogtoClient) SignIn(options *SignInOptions) (string, error) {
oidcConfig, fetchOidcConfigErr := logtoClient.fetchOidcConfig()

if fetchOidcConfigErr != nil {
Expand All @@ -24,23 +34,34 @@ func (logtoClient *LogtoClient) SignIn(redirectUri string) (string, error) {
codeChallenge := core.GenerateCodeChallenge(codeVerifier)
state := core.GenerateState()

prompt := options.Prompt
if prompt == "" {
prompt = logtoClient.logtoConfig.Prompt
}

signInUri, generateSignInUriErr := core.GenerateSignInUri(&core.SignInUriGenerationOptions{
AuthorizationEndpoint: oidcConfig.AuthorizationEndpoint,
ClientId: logtoClient.logtoConfig.AppId,
RedirectUri: redirectUri,
RedirectUri: options.RedirectUri,
CodeChallenge: codeChallenge,
State: state,
Scopes: logtoClient.logtoConfig.Scopes,
Resources: logtoClient.logtoConfig.Resources,
Prompt: logtoClient.logtoConfig.Prompt,
Prompt: prompt,
FirstScreen: options.FirstScreen,
Identifiers: options.Identifiers,
DirectSignIn: options.DirectSignIn,
LoginHint: options.LoginHint,
ExtraParams: options.ExtraParams,
IncludeReservedScopes: logtoClient.logtoConfig.IncludeReservedScopes,
})

if generateSignInUriErr != nil {
return "", generateSignInUriErr
}

signInSession := SignInSession{
RedirectUri: redirectUri,
RedirectUri: options.RedirectUri,
CodeVerifier: codeVerifier,
CodeChallenge: codeChallenge,
State: state,
Expand All @@ -55,3 +76,9 @@ func (logtoClient *LogtoClient) SignIn(redirectUri string) (string, error) {

return signInUri, nil
}

func (logtoClient *LogtoClient) SignInWithRedirectUri(redirectUri string) (string, error) {
return logtoClient.SignIn(&SignInOptions{
RedirectUri: redirectUri,
})
}
2 changes: 1 addition & 1 deletion client/sign_in_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func TestSignInShouldReturnSignInUriCorrectly(t *testing.T) {

logtoClient := NewLogtoClient(logtoConfig, storage)

signInUri, signInErr := logtoClient.SignIn(testRedirectUri)
signInUri, signInErr := logtoClient.SignInWithRedirectUri(testRedirectUri)
assert.Nil(t, signInErr)
assert.Equal(t, testSignInUri, signInUri)

Expand Down
51 changes: 48 additions & 3 deletions core/constants.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package core

var (
const (
ReservedScopeOpenId = "openid"
ReservedScopeOfflineAccess = "offline_access"
)

var (
const (
ReservedResourceOrganization = "urn:logto:resource:organizations"
)

var (
const (
UserScopeProfile = "profile"
UserScopeEmail = "email"
UserScopePhone = "phone"
Expand All @@ -27,3 +27,48 @@ var (
UserScopeProfile,
}
)

const (
QueryKeyClientID = "client_id"
QueryKeyRedirectURI = "redirect_uri"
QueryKeyCodeChallenge = "code_challenge"
QueryKeyCodeChallengeMethod = "code_challenge_method"
QueryKeyState = "state"
QueryKeyScope = "scope"
QueryKeyResource = "resource"
QueryKeyResponseType = "response_type"
QueryKeyPrompt = "prompt"
QueryKeyLoginHint = "login_hint"
QueryKeyFirstScreen = "first_screen"
QueryKeyIdentifiers = "identifiers"
QueryKeyDirectSignIn = "direct_sign_in"
)

const (
IdentifierEmail = "email"
IdentifierPhone = "phone"
IdentifierUsername = "username"
)

const (
DirectSignInMethodSocial = "social"
DirectSignInMethodSso = "sso"
)

const (
PromptConsent = "consent"
PromptLogin = "login"
)

const (
ResponseTypeCode = "code"
)

const (
FirstScreenSignIn = "sign_in"
FirstScreenRegister = "register"
FirstScreenResetPassword = "reset_password"
FirstScreenSingleSignOn = "single_sign_on"
FirstScreenIdentifierSignIn = "identifier:sign_in"
FirstScreenIdentifierRegister = "identifier:register"
)
63 changes: 50 additions & 13 deletions core/sign_in.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ import (
"strings"
)

type DirectSignInOptions struct {
Method string
Target string
}

type SignInUriGenerationOptions struct {
AuthorizationEndpoint string
ClientId string
Expand All @@ -15,6 +20,12 @@ type SignInUriGenerationOptions struct {
Scopes []string
Resources []string
Prompt string
LoginHint string
FirstScreen string
Identifiers []string
DirectSignIn *DirectSignInOptions
ExtraParams map[string]string
IncludeReservedScopes *bool
}

func GenerateSignInUri(option *SignInUriGenerationOptions) (string, error) {
Expand All @@ -26,18 +37,21 @@ func GenerateSignInUri(option *SignInUriGenerationOptions) (string, error) {

queries := uri.Query()

queries.Add("client_id", option.ClientId)
queries.Add("redirect_uri", option.RedirectUri)
queries.Add("code_challenge", option.CodeChallenge)
queries.Add("code_challenge_method", "S256")
queries.Add("state", option.State)
queries.Add(QueryKeyClientID, option.ClientId)
queries.Add(QueryKeyRedirectURI, option.RedirectUri)
queries.Add(QueryKeyCodeChallenge, option.CodeChallenge)
queries.Add(QueryKeyCodeChallengeMethod, "S256")
queries.Add(QueryKeyState, option.State)

scopes := option.Scopes
for _, defaultScope := range DefaultScopes {
scopes = AppendIfNotExisted(scopes, defaultScope)
if option.IncludeReservedScopes == nil || *option.IncludeReservedScopes {
for _, defaultScope := range DefaultScopes {
scopes = AppendIfNotExisted(scopes, defaultScope)
}
}
if len(scopes) != 0 {
queries.Add(QueryKeyScope, strings.Join(scopes, " "))
}

queries.Add("scope", strings.Join(scopes, " "))

resources := option.Resources
if slices.Contains(scopes, UserScopeOrganizations) {
Expand All @@ -46,16 +60,39 @@ func GenerateSignInUri(option *SignInUriGenerationOptions) (string, error) {

if len(resources) != 0 {
for _, resource := range resources {
queries.Add("resource", resource)
queries.Add(QueryKeyResource, resource)
}
}

queries.Add("response_type", "code")
queries.Add(QueryKeyResponseType, ResponseTypeCode)

if option.Prompt != "" {
queries.Add("prompt", option.Prompt)
queries.Add(QueryKeyPrompt, option.Prompt)
} else {
queries.Add("prompt", "consent")
queries.Add(QueryKeyPrompt, PromptConsent)
}

if option.LoginHint != "" {
queries.Add(QueryKeyLoginHint, option.LoginHint)
}

if option.FirstScreen != "" {
queries.Add(QueryKeyFirstScreen, option.FirstScreen)
}

if len(option.Identifiers) != 0 {
queries.Add(QueryKeyIdentifiers, strings.Join(option.Identifiers, " "))
}

if option.DirectSignIn != nil {
directSignInValue := option.DirectSignIn.Method + ":" + option.DirectSignIn.Target
queries.Add(QueryKeyDirectSignIn, directSignInValue)
}

if len(option.ExtraParams) != 0 {
for key, value := range option.ExtraParams {
queries.Add(key, value)
}
}

unescapedQueries, unescapeQueryErr := url.QueryUnescape(queries.Encode())
Expand Down
2 changes: 1 addition & 1 deletion gin-sample/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func main() {
router.GET("/sign-in", func(ctx *gin.Context) {
session := sessions.Default(ctx)
logtoClient := client.NewLogtoClient(logtoConfig, &SessionStorage{session: session})
signInUri, err := logtoClient.SignIn(os.Getenv("REDIRECT_URI"))
signInUri, err := logtoClient.SignInWithRedirectUri(os.Getenv("REDIRECT_URI"))
if err != nil {
ctx.String(http.StatusInternalServerError, err.Error())
return
Expand Down

0 comments on commit b96374e

Please sign in to comment.