Skip to content

Commit 128a2a8

Browse files
committed
feat: add support for response mode
1 parent 7b09a88 commit 128a2a8

File tree

4 files changed

+163
-110
lines changed

4 files changed

+163
-110
lines changed

server/crypto/common.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ func EncryptEnvData(data envstore.Store) (string, error) {
9494
if err != nil {
9595
return "", err
9696
}
97+
9798
encryptedConfig, err := EncryptAESEnv(configData)
9899
if err != nil {
99100
return "", err

server/env/persist_env.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ func PersistEnv() error {
112112

113113
for key, value := range storeData.StringEnv {
114114
// don't override unexposed envs
115-
if key != constants.EnvKeyEncryptionKey && key != constants.EnvKeyClientID && key != constants.EnvKeyClientSecret && key != constants.EnvKeyJWK {
115+
if key != constants.EnvKeyEncryptionKey {
116116
// check only for derivative keys
117117
// No need to check for ENCRYPTION_KEY which special key we use for encrypting config data
118118
// as we have removed it from json

server/handlers/authorize.go

Lines changed: 161 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,20 @@ import (
66

77
"github.com/authorizerdev/authorizer/server/constants"
88
"github.com/authorizerdev/authorizer/server/cookie"
9+
"github.com/authorizerdev/authorizer/server/crypto"
910
"github.com/authorizerdev/authorizer/server/db"
1011
"github.com/authorizerdev/authorizer/server/envstore"
1112
"github.com/authorizerdev/authorizer/server/sessionstore"
1213
"github.com/authorizerdev/authorizer/server/token"
14+
"github.com/authorizerdev/authorizer/server/utils"
1315
"github.com/gin-gonic/gin"
1416
"github.com/google/uuid"
1517
)
1618

1719
// AuthorizeHandler is the handler for the /authorize route
1820
// required params
1921
// ?redirect_uri = redirect url
22+
// ?response_mode = to decide if result should be html or re-direct
2023
// state[recommended] = to prevent CSRF attack (for authorizer its compulsory)
2124
// code_challenge = to prevent CSRF attack
2225
// code_challenge_method = to prevent CSRF attack [only sh256 is supported]
@@ -31,56 +34,74 @@ func AuthorizeHandler() gin.HandlerFunc {
3134
scopeString := strings.TrimSpace(gc.Query("scope"))
3235
clientID := strings.TrimSpace(gc.Query("client_id"))
3336
template := "authorize.tmpl"
37+
responseMode := strings.TrimSpace(gc.Query("response_mode"))
3438

35-
if clientID == "" {
36-
gc.HTML(http.StatusOK, template, gin.H{
37-
"target_origin": redirectURI,
38-
"authorization_response": map[string]interface{}{
39-
"type": "authorization_response",
40-
"response": map[string]string{
41-
"error": "client_id is required",
42-
},
43-
},
44-
})
45-
return
39+
if responseMode == "" {
40+
responseMode = "query"
4641
}
4742

48-
if clientID != envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyClientID) {
49-
gc.HTML(http.StatusOK, template, gin.H{
50-
"target_origin": redirectURI,
51-
"authorization_response": map[string]interface{}{
52-
"type": "authorization_response",
53-
"response": map[string]string{
54-
"error": "invalid_client_id",
43+
if responseMode != "query" && responseMode != "web_message" {
44+
gc.JSON(400, gin.H{"error": "invalid response mode"})
45+
}
46+
47+
if redirectURI == "" {
48+
redirectURI = "/app"
49+
}
50+
51+
isQuery := responseMode == "query"
52+
53+
hostname := utils.GetHost(gc)
54+
loginRedirectState := crypto.EncryptB64(`{"authorizerURL":"` + hostname + `","redirectURL":"` + redirectURI + `"}`)
55+
loginURL := "/app?state=" + loginRedirectState
56+
57+
if clientID == "" {
58+
if isQuery {
59+
gc.Redirect(http.StatusFound, loginURL)
60+
} else {
61+
gc.HTML(http.StatusOK, template, gin.H{
62+
"target_origin": redirectURI,
63+
"authorization_response": map[string]interface{}{
64+
"type": "authorization_response",
65+
"response": map[string]string{
66+
"error": "client_id is required",
67+
},
5568
},
56-
},
57-
})
69+
})
70+
}
5871
return
5972
}
6073

61-
if redirectURI == "" {
62-
gc.HTML(http.StatusOK, template, gin.H{
63-
"target_origin": redirectURI,
64-
"authorization_response": map[string]interface{}{
65-
"type": "authorization_response",
66-
"response": map[string]string{
67-
"error": "redirect_uri is required",
74+
if clientID != envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyClientID) {
75+
if isQuery {
76+
gc.Redirect(http.StatusFound, loginURL)
77+
} else {
78+
gc.HTML(http.StatusOK, template, gin.H{
79+
"target_origin": redirectURI,
80+
"authorization_response": map[string]interface{}{
81+
"type": "authorization_response",
82+
"response": map[string]string{
83+
"error": "invalid_client_id",
84+
},
6885
},
69-
},
70-
})
86+
})
87+
}
7188
return
7289
}
7390

7491
if state == "" {
75-
gc.HTML(http.StatusOK, template, gin.H{
76-
"target_origin": redirectURI,
77-
"authorization_response": map[string]interface{}{
78-
"type": "authorization_response",
79-
"response": map[string]string{
80-
"error": "state is required",
92+
if isQuery {
93+
gc.Redirect(http.StatusFound, loginURL)
94+
} else {
95+
gc.HTML(http.StatusOK, template, gin.H{
96+
"target_origin": redirectURI,
97+
"authorization_response": map[string]interface{}{
98+
"type": "authorization_response",
99+
"response": map[string]string{
100+
"error": "state is required",
101+
},
81102
},
82-
},
83-
})
103+
})
104+
}
84105
return
85106
}
86107

@@ -99,76 +120,96 @@ func AuthorizeHandler() gin.HandlerFunc {
99120
isResponseTypeToken := responseType == "token"
100121

101122
if !isResponseTypeCode && !isResponseTypeToken {
102-
gc.HTML(http.StatusOK, template, gin.H{
103-
"target_origin": redirectURI,
104-
"authorization_response": map[string]interface{}{
105-
"type": "authorization_response",
106-
"response": map[string]string{
107-
"error": "response_type is invalid",
108-
},
109-
},
110-
})
111-
return
112-
}
113-
114-
if isResponseTypeCode {
115-
if codeChallenge == "" {
116-
gc.HTML(http.StatusBadRequest, template, gin.H{
123+
if isQuery {
124+
gc.Redirect(http.StatusFound, loginURL)
125+
} else {
126+
gc.HTML(http.StatusOK, template, gin.H{
117127
"target_origin": redirectURI,
118128
"authorization_response": map[string]interface{}{
119129
"type": "authorization_response",
120130
"response": map[string]string{
121-
"error": "code_challenge is required",
131+
"error": "response_type is invalid",
122132
},
123133
},
124134
})
135+
}
136+
return
137+
}
138+
139+
if isResponseTypeCode {
140+
if codeChallenge == "" {
141+
if isQuery {
142+
gc.Redirect(http.StatusFound, loginURL)
143+
} else {
144+
gc.HTML(http.StatusBadRequest, template, gin.H{
145+
"target_origin": redirectURI,
146+
"authorization_response": map[string]interface{}{
147+
"type": "authorization_response",
148+
"response": map[string]string{
149+
"error": "code_challenge is required",
150+
},
151+
},
152+
})
153+
}
125154
return
126155
}
127156
}
128157

129158
sessionToken, err := cookie.GetSession(gc)
130159
if err != nil {
131-
gc.HTML(http.StatusOK, template, gin.H{
132-
"target_origin": redirectURI,
133-
"authorization_response": map[string]interface{}{
134-
"type": "authorization_response",
135-
"response": map[string]string{
136-
"error": "login_required",
137-
"error_description": "Login is required",
160+
if isQuery {
161+
gc.Redirect(http.StatusFound, loginURL)
162+
} else {
163+
gc.HTML(http.StatusOK, template, gin.H{
164+
"target_origin": redirectURI,
165+
"authorization_response": map[string]interface{}{
166+
"type": "authorization_response",
167+
"response": map[string]string{
168+
"error": "login_required",
169+
"error_description": "Login is required",
170+
},
138171
},
139-
},
140-
})
172+
})
173+
}
141174
return
142175
}
143176

144177
// get session from cookie
145178
claims, err := token.ValidateBrowserSession(gc, sessionToken)
146179
if err != nil {
147-
gc.HTML(http.StatusOK, template, gin.H{
148-
"target_origin": redirectURI,
149-
"authorization_response": map[string]interface{}{
150-
"type": "authorization_response",
151-
"response": map[string]string{
152-
"error": "login_required",
153-
"error_description": "Login is required",
180+
if isQuery {
181+
gc.Redirect(http.StatusFound, loginURL)
182+
} else {
183+
gc.HTML(http.StatusOK, template, gin.H{
184+
"target_origin": redirectURI,
185+
"authorization_response": map[string]interface{}{
186+
"type": "authorization_response",
187+
"response": map[string]string{
188+
"error": "login_required",
189+
"error_description": "Login is required",
190+
},
154191
},
155-
},
156-
})
192+
})
193+
}
157194
return
158195
}
159196
userID := claims.Subject
160197
user, err := db.Provider.GetUserByID(userID)
161198
if err != nil {
162-
gc.HTML(http.StatusOK, template, gin.H{
163-
"target_origin": redirectURI,
164-
"authorization_response": map[string]interface{}{
165-
"type": "authorization_response",
166-
"response": map[string]string{
167-
"error": "signup_required",
168-
"error_description": "Sign up required",
199+
if isQuery {
200+
gc.Redirect(http.StatusFound, loginURL)
201+
} else {
202+
gc.HTML(http.StatusOK, template, gin.H{
203+
"target_origin": redirectURI,
204+
"authorization_response": map[string]interface{}{
205+
"type": "authorization_response",
206+
"response": map[string]string{
207+
"error": "signup_required",
208+
"error_description": "Sign up required",
209+
},
169210
},
170-
},
171-
})
211+
})
212+
}
172213
return
173214
}
174215

@@ -180,16 +221,20 @@ func AuthorizeHandler() gin.HandlerFunc {
180221
nonce := uuid.New().String()
181222
newSessionTokenData, newSessionToken, err := token.CreateSessionToken(user, nonce, claims.Roles, scope)
182223
if err != nil {
183-
gc.HTML(http.StatusOK, template, gin.H{
184-
"target_origin": redirectURI,
185-
"authorization_response": map[string]interface{}{
186-
"type": "authorization_response",
187-
"response": map[string]string{
188-
"error": "login_required",
189-
"error_description": "Login is required",
224+
if isQuery {
225+
gc.Redirect(http.StatusFound, loginURL)
226+
} else {
227+
gc.HTML(http.StatusOK, template, gin.H{
228+
"target_origin": redirectURI,
229+
"authorization_response": map[string]interface{}{
230+
"type": "authorization_response",
231+
"response": map[string]string{
232+
"error": "login_required",
233+
"error_description": "Login is required",
234+
},
190235
},
191-
},
192-
})
236+
})
237+
}
193238
return
194239
}
195240

@@ -214,16 +259,20 @@ func AuthorizeHandler() gin.HandlerFunc {
214259
// rollover the session for security
215260
authToken, err := token.CreateAuthToken(gc, user, claims.Roles, scope)
216261
if err != nil {
217-
gc.HTML(http.StatusOK, template, gin.H{
218-
"target_origin": redirectURI,
219-
"authorization_response": map[string]interface{}{
220-
"type": "authorization_response",
221-
"response": map[string]string{
222-
"error": "login_required",
223-
"error_description": "Login is required",
262+
if isQuery {
263+
gc.Redirect(http.StatusFound, loginURL)
264+
} else {
265+
gc.HTML(http.StatusOK, template, gin.H{
266+
"target_origin": redirectURI,
267+
"authorization_response": map[string]interface{}{
268+
"type": "authorization_response",
269+
"response": map[string]string{
270+
"error": "login_required",
271+
"error_description": "Login is required",
272+
},
224273
},
225-
},
226-
})
274+
})
275+
}
227276
return
228277
}
229278
sessionstore.RemoveState(sessionToken)
@@ -256,16 +305,20 @@ func AuthorizeHandler() gin.HandlerFunc {
256305
return
257306
}
258307

259-
// by default return with error
260-
gc.HTML(http.StatusOK, template, gin.H{
261-
"target_origin": redirectURI,
262-
"authorization_response": map[string]interface{}{
263-
"type": "authorization_response",
264-
"response": map[string]string{
265-
"error": "login_required",
266-
"error_description": "Login is required",
308+
if isQuery {
309+
gc.Redirect(http.StatusFound, loginURL)
310+
} else {
311+
// by default return with error
312+
gc.HTML(http.StatusOK, template, gin.H{
313+
"target_origin": redirectURI,
314+
"authorization_response": map[string]interface{}{
315+
"type": "authorization_response",
316+
"response": map[string]string{
317+
"error": "login_required",
318+
"error_description": "Login is required",
319+
},
267320
},
268-
},
269-
})
321+
})
322+
}
270323
}
271324
}

templates/authorize.tmpl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
(function (window, document) {
99
var targetOrigin = {{.target_origin}};
1010
var authorizationResponse = {{.authorization_response}};
11-
console.log({targetOrigin})
1211
window.parent.postMessage(authorizationResponse, targetOrigin);
1312
})(this, this.document);
1413
</script>

0 commit comments

Comments
 (0)