Compare commits
16 Commits
0.14.0-bet
...
0.14.0-bet
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d1e284116d | ||
![]() |
2f9725d8e1 | ||
![]() |
ee7aea7bee | ||
![]() |
5d73df0040 | ||
![]() |
60cd317e67 | ||
![]() |
f5bdc8db39 | ||
![]() |
9eca697a91 | ||
![]() |
7136ee924d | ||
![]() |
fd9eb7c733 | ||
![]() |
917eaeb2ed | ||
![]() |
3bb90acc9e | ||
![]() |
a69b8e290c | ||
![]() |
674eeeea4e | ||
![]() |
8c2bf6ee0d | ||
![]() |
57bc091499 | ||
![]() |
128a2a8f75 |
30
app/package-lock.json
generated
30
app/package-lock.json
generated
@@ -9,7 +9,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@authorizerdev/authorizer-react": "0.7.0",
|
"@authorizerdev/authorizer-react": "0.9.0-beta.3",
|
||||||
"@types/react": "^17.0.15",
|
"@types/react": "^17.0.15",
|
||||||
"@types/react-dom": "^17.0.9",
|
"@types/react-dom": "^17.0.9",
|
||||||
"esbuild": "^0.12.17",
|
"esbuild": "^0.12.17",
|
||||||
@@ -24,9 +24,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@authorizerdev/authorizer-js": {
|
"node_modules/@authorizerdev/authorizer-js": {
|
||||||
"version": "0.3.0",
|
"version": "0.4.0-beta.0",
|
||||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.4.0-beta.0.tgz",
|
||||||
"integrity": "sha512-KCE5Dw5MUnEgstBUayBriDQAOjqbxU7ixC00rTHAE6aD6TxJkeSls0vCTXpvt4iiKhFK6q9BhHwa/5NwWYpDBQ==",
|
"integrity": "sha512-wNh5ROldNqdbOXFPDlq1tObzPZyEQkbnOvSEwvnDfPYb9/BsJ3naj3/ayz4J2R5k2+Eyuk0LK64XYdkfIW0HYA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"node-fetch": "^2.6.1"
|
"node-fetch": "^2.6.1"
|
||||||
},
|
},
|
||||||
@@ -35,11 +35,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@authorizerdev/authorizer-react": {
|
"node_modules/@authorizerdev/authorizer-react": {
|
||||||
"version": "0.7.0",
|
"version": "0.9.0-beta.3",
|
||||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.9.0-beta.3.tgz",
|
||||||
"integrity": "sha512-cAxUhodftIveSQt+rFuEA0CxjmbpVfE43ioZBwBxqWEuJHPdPH7bohOQRgTyA2xb3QVnh7kr607Tau13DO7qUA==",
|
"integrity": "sha512-P93PW6W3Qm9BW3160gn0Ce+64UCFAOpoEOHf5537LgFPE8LpNAIU3EI6EtMNkOJS58pu1h2UkfyRyX/j0Pohjw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@authorizerdev/authorizer-js": "^0.3.0",
|
"@authorizerdev/authorizer-js": "^0.4.0-beta.0",
|
||||||
"final-form": "^4.20.2",
|
"final-form": "^4.20.2",
|
||||||
"react-final-form": "^6.5.3",
|
"react-final-form": "^6.5.3",
|
||||||
"styled-components": "^5.3.0"
|
"styled-components": "^5.3.0"
|
||||||
@@ -829,19 +829,19 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@authorizerdev/authorizer-js": {
|
"@authorizerdev/authorizer-js": {
|
||||||
"version": "0.3.0",
|
"version": "0.4.0-beta.0",
|
||||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.4.0-beta.0.tgz",
|
||||||
"integrity": "sha512-KCE5Dw5MUnEgstBUayBriDQAOjqbxU7ixC00rTHAE6aD6TxJkeSls0vCTXpvt4iiKhFK6q9BhHwa/5NwWYpDBQ==",
|
"integrity": "sha512-wNh5ROldNqdbOXFPDlq1tObzPZyEQkbnOvSEwvnDfPYb9/BsJ3naj3/ayz4J2R5k2+Eyuk0LK64XYdkfIW0HYA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"node-fetch": "^2.6.1"
|
"node-fetch": "^2.6.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@authorizerdev/authorizer-react": {
|
"@authorizerdev/authorizer-react": {
|
||||||
"version": "0.7.0",
|
"version": "0.9.0-beta.3",
|
||||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.9.0-beta.3.tgz",
|
||||||
"integrity": "sha512-cAxUhodftIveSQt+rFuEA0CxjmbpVfE43ioZBwBxqWEuJHPdPH7bohOQRgTyA2xb3QVnh7kr607Tau13DO7qUA==",
|
"integrity": "sha512-P93PW6W3Qm9BW3160gn0Ce+64UCFAOpoEOHf5537LgFPE8LpNAIU3EI6EtMNkOJS58pu1h2UkfyRyX/j0Pohjw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@authorizerdev/authorizer-js": "^0.3.0",
|
"@authorizerdev/authorizer-js": "^0.4.0-beta.0",
|
||||||
"final-form": "^4.20.2",
|
"final-form": "^4.20.2",
|
||||||
"react-final-form": "^6.5.3",
|
"react-final-form": "^6.5.3",
|
||||||
"styled-components": "^5.3.0"
|
"styled-components": "^5.3.0"
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
"author": "Lakhan Samani",
|
"author": "Lakhan Samani",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@authorizerdev/authorizer-react": "latest",
|
"@authorizerdev/authorizer-react": "0.9.0-beta.3",
|
||||||
"@types/react": "^17.0.15",
|
"@types/react": "^17.0.15",
|
||||||
"@types/react-dom": "^17.0.9",
|
"@types/react-dom": "^17.0.9",
|
||||||
"esbuild": "^0.12.17",
|
"esbuild": "^0.12.17",
|
||||||
|
@@ -30,15 +30,7 @@ export default function App() {
|
|||||||
/>
|
/>
|
||||||
<h1>{globalState.organizationName}</h1>
|
<h1>{globalState.organizationName}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div className="container">
|
||||||
style={{
|
|
||||||
width: 400,
|
|
||||||
margin: `10px auto`,
|
|
||||||
border: `1px solid #D1D5DB`,
|
|
||||||
padding: `25px 20px`,
|
|
||||||
borderRadius: 5,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<AuthorizerProvider
|
<AuthorizerProvider
|
||||||
config={{
|
config={{
|
||||||
|
@@ -11,9 +11,19 @@ export default function Root() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (token) {
|
if (token) {
|
||||||
const url = new URL(config.redirectURL || '/app');
|
console.log({ token });
|
||||||
|
let redirectURL = config.redirectURL || '/app';
|
||||||
|
const params = `access_token=${token.access_token}&id_token=${token.id_token}&expires_in=${token.expires_in}&refresh_token=${token.refresh_token}`;
|
||||||
|
const url = new URL(redirectURL);
|
||||||
|
if (redirectURL.includes('?')) {
|
||||||
|
redirectURL = `${redirectURL}&${params}`;
|
||||||
|
} else {
|
||||||
|
redirectURL = `${redirectURL}?${params}`;
|
||||||
|
}
|
||||||
|
|
||||||
if (url.origin !== window.location.origin) {
|
if (url.origin !== window.location.origin) {
|
||||||
window.location.href = config.redirectURL || '/app';
|
sessionStorage.removeItem('authorizer_state');
|
||||||
|
window.location.replace(redirectURL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return () => {};
|
return () => {};
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 10;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||||
sans-serif;
|
sans-serif;
|
||||||
@@ -14,3 +14,17 @@ body {
|
|||||||
*:after {
|
*:after {
|
||||||
box-sizing: inherit;
|
box-sizing: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
box-sizing: content-box;
|
||||||
|
border: 1px solid #d1d5db;
|
||||||
|
padding: 25px 20px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (min-width: 768px) {
|
||||||
|
.container {
|
||||||
|
width: 400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -94,6 +94,7 @@ func EncryptEnvData(data envstore.Store) (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
encryptedConfig, err := EncryptAESEnv(configData)
|
encryptedConfig, err := EncryptAESEnv(configData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@@ -4,25 +4,28 @@ import "github.com/authorizerdev/authorizer/server/graph/model"
|
|||||||
|
|
||||||
// VerificationRequest model for db
|
// VerificationRequest model for db
|
||||||
type VerificationRequest struct {
|
type VerificationRequest struct {
|
||||||
Key string `json:"_key,omitempty" bson:"_key"` // for arangodb
|
Key string `json:"_key,omitempty" bson:"_key"` // for arangodb
|
||||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id"`
|
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id"`
|
||||||
Token string `gorm:"type:text" json:"token" bson:"token"`
|
Token string `gorm:"type:text" json:"token" bson:"token"`
|
||||||
Identifier string `gorm:"uniqueIndex:idx_email_identifier" json:"identifier" bson:"identifier"`
|
Identifier string `gorm:"uniqueIndex:idx_email_identifier" json:"identifier" bson:"identifier"`
|
||||||
ExpiresAt int64 `json:"expires_at" bson:"expires_at"`
|
ExpiresAt int64 `json:"expires_at" bson:"expires_at"`
|
||||||
CreatedAt int64 `json:"created_at" bson:"created_at"`
|
CreatedAt int64 `json:"created_at" bson:"created_at"`
|
||||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at"`
|
UpdatedAt int64 `json:"updated_at" bson:"updated_at"`
|
||||||
Email string `gorm:"uniqueIndex:idx_email_identifier" json:"email" bson:"email"`
|
Email string `gorm:"uniqueIndex:idx_email_identifier" json:"email" bson:"email"`
|
||||||
Nonce string `gorm:"type:char(36)" json:"nonce" bson:"nonce"`
|
Nonce string `gorm:"type:text" json:"nonce" bson:"nonce"`
|
||||||
|
RedirectURI string `gorm:"type:text" json:"redirect_uri" bson:"redirect_uri"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *VerificationRequest) AsAPIVerificationRequest() *model.VerificationRequest {
|
func (v *VerificationRequest) AsAPIVerificationRequest() *model.VerificationRequest {
|
||||||
return &model.VerificationRequest{
|
return &model.VerificationRequest{
|
||||||
ID: v.ID,
|
ID: v.ID,
|
||||||
Token: &v.Token,
|
Token: &v.Token,
|
||||||
Identifier: &v.Identifier,
|
Identifier: &v.Identifier,
|
||||||
Expires: &v.ExpiresAt,
|
Expires: &v.ExpiresAt,
|
||||||
CreatedAt: &v.CreatedAt,
|
CreatedAt: &v.CreatedAt,
|
||||||
UpdatedAt: &v.UpdatedAt,
|
UpdatedAt: &v.UpdatedAt,
|
||||||
Email: &v.Email,
|
Email: &v.Email,
|
||||||
|
Nonce: &v.Nonce,
|
||||||
|
RedirectURI: &v.RedirectURI,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -21,7 +21,7 @@ func (p *provider) AddVerificationRequest(verificationRequest models.Verificatio
|
|||||||
verificationRequest.UpdatedAt = time.Now().Unix()
|
verificationRequest.UpdatedAt = time.Now().Unix()
|
||||||
result := p.db.Clauses(clause.OnConflict{
|
result := p.db.Clauses(clause.OnConflict{
|
||||||
Columns: []clause.Column{{Name: "email"}, {Name: "identifier"}},
|
Columns: []clause.Column{{Name: "email"}, {Name: "identifier"}},
|
||||||
DoUpdates: clause.AssignmentColumns([]string{"token", "expires_at"}),
|
DoUpdates: clause.AssignmentColumns([]string{"token", "expires_at", "nonce", "redirect_uri"}),
|
||||||
}).Create(&verificationRequest)
|
}).Create(&verificationRequest)
|
||||||
|
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
package email
|
package email
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/envstore"
|
"github.com/authorizerdev/authorizer/server/envstore"
|
||||||
)
|
)
|
||||||
@@ -103,5 +105,9 @@ func SendVerificationMail(toEmail, token, hostname string) error {
|
|||||||
message = addEmailTemplate(message, data, "verify_email.tmpl")
|
message = addEmailTemplate(message, data, "verify_email.tmpl")
|
||||||
// bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message)
|
// bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message)
|
||||||
|
|
||||||
return SendMail(Receiver, Subject, message)
|
err := SendMail(Receiver, Subject, message)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("=> error sending email:", err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
2
server/env/persist_env.go
vendored
2
server/env/persist_env.go
vendored
@@ -112,7 +112,7 @@ func PersistEnv() error {
|
|||||||
|
|
||||||
for key, value := range storeData.StringEnv {
|
for key, value := range storeData.StringEnv {
|
||||||
// don't override unexposed envs
|
// don't override unexposed envs
|
||||||
if key != constants.EnvKeyEncryptionKey && key != constants.EnvKeyClientID && key != constants.EnvKeyClientSecret && key != constants.EnvKeyJWK {
|
if key != constants.EnvKeyEncryptionKey {
|
||||||
// check only for derivative keys
|
// check only for derivative keys
|
||||||
// No need to check for ENCRYPTION_KEY which special key we use for encrypting config data
|
// No need to check for ENCRYPTION_KEY which special key we use for encrypting config data
|
||||||
// as we have removed it from json
|
// as we have removed it from json
|
||||||
|
@@ -119,6 +119,7 @@ type ComplexityRoot struct {
|
|||||||
MagicLinkLogin func(childComplexity int, params model.MagicLinkLoginInput) int
|
MagicLinkLogin func(childComplexity int, params model.MagicLinkLoginInput) int
|
||||||
ResendVerifyEmail func(childComplexity int, params model.ResendVerifyEmailInput) int
|
ResendVerifyEmail func(childComplexity int, params model.ResendVerifyEmailInput) int
|
||||||
ResetPassword func(childComplexity int, params model.ResetPasswordInput) int
|
ResetPassword func(childComplexity int, params model.ResetPasswordInput) int
|
||||||
|
Revoke func(childComplexity int, params model.OAuthRevokeInput) int
|
||||||
Signup func(childComplexity int, params model.SignUpInput) int
|
Signup func(childComplexity int, params model.SignUpInput) int
|
||||||
UpdateEnv func(childComplexity int, params model.UpdateEnvInput) int
|
UpdateEnv func(childComplexity int, params model.UpdateEnvInput) int
|
||||||
UpdateProfile func(childComplexity int, params model.UpdateProfileInput) int
|
UpdateProfile func(childComplexity int, params model.UpdateProfileInput) int
|
||||||
@@ -173,13 +174,15 @@ type ComplexityRoot struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
VerificationRequest struct {
|
VerificationRequest struct {
|
||||||
CreatedAt func(childComplexity int) int
|
CreatedAt func(childComplexity int) int
|
||||||
Email func(childComplexity int) int
|
Email func(childComplexity int) int
|
||||||
Expires func(childComplexity int) int
|
Expires func(childComplexity int) int
|
||||||
ID func(childComplexity int) int
|
ID func(childComplexity int) int
|
||||||
Identifier func(childComplexity int) int
|
Identifier func(childComplexity int) int
|
||||||
Token func(childComplexity int) int
|
Nonce func(childComplexity int) int
|
||||||
UpdatedAt func(childComplexity int) int
|
RedirectURI func(childComplexity int) int
|
||||||
|
Token func(childComplexity int) int
|
||||||
|
UpdatedAt func(childComplexity int) int
|
||||||
}
|
}
|
||||||
|
|
||||||
VerificationRequests struct {
|
VerificationRequests struct {
|
||||||
@@ -198,6 +201,7 @@ type MutationResolver interface {
|
|||||||
ResendVerifyEmail(ctx context.Context, params model.ResendVerifyEmailInput) (*model.Response, error)
|
ResendVerifyEmail(ctx context.Context, params model.ResendVerifyEmailInput) (*model.Response, error)
|
||||||
ForgotPassword(ctx context.Context, params model.ForgotPasswordInput) (*model.Response, error)
|
ForgotPassword(ctx context.Context, params model.ForgotPasswordInput) (*model.Response, error)
|
||||||
ResetPassword(ctx context.Context, params model.ResetPasswordInput) (*model.Response, error)
|
ResetPassword(ctx context.Context, params model.ResetPasswordInput) (*model.Response, error)
|
||||||
|
Revoke(ctx context.Context, params model.OAuthRevokeInput) (*model.Response, error)
|
||||||
DeleteUser(ctx context.Context, params model.DeleteUserInput) (*model.Response, error)
|
DeleteUser(ctx context.Context, params model.DeleteUserInput) (*model.Response, error)
|
||||||
UpdateUser(ctx context.Context, params model.UpdateUserInput) (*model.User, error)
|
UpdateUser(ctx context.Context, params model.UpdateUserInput) (*model.User, error)
|
||||||
AdminSignup(ctx context.Context, params model.AdminSignupInput) (*model.Response, error)
|
AdminSignup(ctx context.Context, params model.AdminSignupInput) (*model.Response, error)
|
||||||
@@ -711,6 +715,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||||||
|
|
||||||
return e.complexity.Mutation.ResetPassword(childComplexity, args["params"].(model.ResetPasswordInput)), true
|
return e.complexity.Mutation.ResetPassword(childComplexity, args["params"].(model.ResetPasswordInput)), true
|
||||||
|
|
||||||
|
case "Mutation.revoke":
|
||||||
|
if e.complexity.Mutation.Revoke == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
args, err := ec.field_Mutation_revoke_args(context.TODO(), rawArgs)
|
||||||
|
if err != nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.complexity.Mutation.Revoke(childComplexity, args["params"].(model.OAuthRevokeInput)), true
|
||||||
|
|
||||||
case "Mutation.signup":
|
case "Mutation.signup":
|
||||||
if e.complexity.Mutation.Signup == nil {
|
if e.complexity.Mutation.Signup == nil {
|
||||||
break
|
break
|
||||||
@@ -1038,6 +1054,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||||||
|
|
||||||
return e.complexity.VerificationRequest.Identifier(childComplexity), true
|
return e.complexity.VerificationRequest.Identifier(childComplexity), true
|
||||||
|
|
||||||
|
case "VerificationRequest.nonce":
|
||||||
|
if e.complexity.VerificationRequest.Nonce == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.complexity.VerificationRequest.Nonce(childComplexity), true
|
||||||
|
|
||||||
|
case "VerificationRequest.redirect_uri":
|
||||||
|
if e.complexity.VerificationRequest.RedirectURI == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.complexity.VerificationRequest.RedirectURI(childComplexity), true
|
||||||
|
|
||||||
case "VerificationRequest.token":
|
case "VerificationRequest.token":
|
||||||
if e.complexity.VerificationRequest.Token == nil {
|
if e.complexity.VerificationRequest.Token == nil {
|
||||||
break
|
break
|
||||||
@@ -1189,6 +1219,8 @@ type VerificationRequest {
|
|||||||
expires: Int64
|
expires: Int64
|
||||||
created_at: Int64
|
created_at: Int64
|
||||||
updated_at: Int64
|
updated_at: Int64
|
||||||
|
nonce: String
|
||||||
|
redirect_uri: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type VerificationRequests {
|
type VerificationRequests {
|
||||||
@@ -1361,6 +1393,8 @@ input UpdateUserInput {
|
|||||||
|
|
||||||
input ForgotPasswordInput {
|
input ForgotPasswordInput {
|
||||||
email: String!
|
email: String!
|
||||||
|
state: String
|
||||||
|
redirect_uri: String
|
||||||
}
|
}
|
||||||
|
|
||||||
input ResetPasswordInput {
|
input ResetPasswordInput {
|
||||||
@@ -1377,6 +1411,8 @@ input MagicLinkLoginInput {
|
|||||||
email: String!
|
email: String!
|
||||||
roles: [String!]
|
roles: [String!]
|
||||||
scope: [String!]
|
scope: [String!]
|
||||||
|
state: String
|
||||||
|
redirect_uri: String
|
||||||
}
|
}
|
||||||
|
|
||||||
input SessionQueryInput {
|
input SessionQueryInput {
|
||||||
@@ -1393,6 +1429,10 @@ input PaginatedInput {
|
|||||||
pagination: PaginationInput
|
pagination: PaginationInput
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input OAuthRevokeInput {
|
||||||
|
refresh_token: String!
|
||||||
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
signup(params: SignUpInput!): AuthResponse!
|
signup(params: SignUpInput!): AuthResponse!
|
||||||
login(params: LoginInput!): AuthResponse!
|
login(params: LoginInput!): AuthResponse!
|
||||||
@@ -1403,6 +1443,7 @@ type Mutation {
|
|||||||
resend_verify_email(params: ResendVerifyEmailInput!): Response!
|
resend_verify_email(params: ResendVerifyEmailInput!): Response!
|
||||||
forgot_password(params: ForgotPasswordInput!): Response!
|
forgot_password(params: ForgotPasswordInput!): Response!
|
||||||
reset_password(params: ResetPasswordInput!): Response!
|
reset_password(params: ResetPasswordInput!): Response!
|
||||||
|
revoke(params: OAuthRevokeInput!): Response!
|
||||||
# admin only apis
|
# admin only apis
|
||||||
_delete_user(params: DeleteUserInput!): Response!
|
_delete_user(params: DeleteUserInput!): Response!
|
||||||
_update_user(params: UpdateUserInput!): User!
|
_update_user(params: UpdateUserInput!): User!
|
||||||
@@ -1580,6 +1621,21 @@ func (ec *executionContext) field_Mutation_reset_password_args(ctx context.Conte
|
|||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) field_Mutation_revoke_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||||
|
var err error
|
||||||
|
args := map[string]interface{}{}
|
||||||
|
var arg0 model.OAuthRevokeInput
|
||||||
|
if tmp, ok := rawArgs["params"]; ok {
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("params"))
|
||||||
|
arg0, err = ec.unmarshalNOAuthRevokeInput2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐOAuthRevokeInput(ctx, tmp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args["params"] = arg0
|
||||||
|
return args, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) field_Mutation_signup_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
func (ec *executionContext) field_Mutation_signup_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||||
var err error
|
var err error
|
||||||
args := map[string]interface{}{}
|
args := map[string]interface{}{}
|
||||||
@@ -3838,6 +3894,48 @@ func (ec *executionContext) _Mutation_reset_password(ctx context.Context, field
|
|||||||
return ec.marshalNResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐResponse(ctx, field.Selections, res)
|
return ec.marshalNResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐResponse(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) _Mutation_revoke(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
ec.Error(ctx, ec.Recover(ctx, r))
|
||||||
|
ret = graphql.Null
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
fc := &graphql.FieldContext{
|
||||||
|
Object: "Mutation",
|
||||||
|
Field: field,
|
||||||
|
Args: nil,
|
||||||
|
IsMethod: true,
|
||||||
|
IsResolver: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = graphql.WithFieldContext(ctx, fc)
|
||||||
|
rawArgs := field.ArgumentMap(ec.Variables)
|
||||||
|
args, err := ec.field_Mutation_revoke_args(ctx, rawArgs)
|
||||||
|
if err != nil {
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
fc.Args = args
|
||||||
|
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||||
|
ctx = rctx // use context from middleware stack in children
|
||||||
|
return ec.resolvers.Mutation().Revoke(rctx, args["params"].(model.OAuthRevokeInput))
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
if resTmp == nil {
|
||||||
|
if !graphql.HasFieldError(ctx, fc) {
|
||||||
|
ec.Errorf(ctx, "must not be null")
|
||||||
|
}
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
res := resTmp.(*model.Response)
|
||||||
|
fc.Result = res
|
||||||
|
return ec.marshalNResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐResponse(ctx, field.Selections, res)
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _Mutation__delete_user(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
func (ec *executionContext) _Mutation__delete_user(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
@@ -5451,6 +5549,70 @@ func (ec *executionContext) _VerificationRequest_updated_at(ctx context.Context,
|
|||||||
return ec.marshalOInt642ᚖint64(ctx, field.Selections, res)
|
return ec.marshalOInt642ᚖint64(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) _VerificationRequest_nonce(ctx context.Context, field graphql.CollectedField, obj *model.VerificationRequest) (ret graphql.Marshaler) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
ec.Error(ctx, ec.Recover(ctx, r))
|
||||||
|
ret = graphql.Null
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
fc := &graphql.FieldContext{
|
||||||
|
Object: "VerificationRequest",
|
||||||
|
Field: field,
|
||||||
|
Args: nil,
|
||||||
|
IsMethod: false,
|
||||||
|
IsResolver: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = graphql.WithFieldContext(ctx, fc)
|
||||||
|
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||||
|
ctx = rctx // use context from middleware stack in children
|
||||||
|
return obj.Nonce, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
if resTmp == nil {
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
res := resTmp.(*string)
|
||||||
|
fc.Result = res
|
||||||
|
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) _VerificationRequest_redirect_uri(ctx context.Context, field graphql.CollectedField, obj *model.VerificationRequest) (ret graphql.Marshaler) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
ec.Error(ctx, ec.Recover(ctx, r))
|
||||||
|
ret = graphql.Null
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
fc := &graphql.FieldContext{
|
||||||
|
Object: "VerificationRequest",
|
||||||
|
Field: field,
|
||||||
|
Args: nil,
|
||||||
|
IsMethod: false,
|
||||||
|
IsResolver: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = graphql.WithFieldContext(ctx, fc)
|
||||||
|
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||||
|
ctx = rctx // use context from middleware stack in children
|
||||||
|
return obj.RedirectURI, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
if resTmp == nil {
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
res := resTmp.(*string)
|
||||||
|
fc.Result = res
|
||||||
|
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _VerificationRequests_pagination(ctx context.Context, field graphql.CollectedField, obj *model.VerificationRequests) (ret graphql.Marshaler) {
|
func (ec *executionContext) _VerificationRequests_pagination(ctx context.Context, field graphql.CollectedField, obj *model.VerificationRequests) (ret graphql.Marshaler) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
@@ -6729,6 +6891,22 @@ func (ec *executionContext) unmarshalInputForgotPasswordInput(ctx context.Contex
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return it, err
|
return it, err
|
||||||
}
|
}
|
||||||
|
case "state":
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("state"))
|
||||||
|
it.State, err = ec.unmarshalOString2ᚖstring(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
|
case "redirect_uri":
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("redirect_uri"))
|
||||||
|
it.RedirectURI, err = ec.unmarshalOString2ᚖstring(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6815,6 +6993,45 @@ func (ec *executionContext) unmarshalInputMagicLinkLoginInput(ctx context.Contex
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return it, err
|
return it, err
|
||||||
}
|
}
|
||||||
|
case "state":
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("state"))
|
||||||
|
it.State, err = ec.unmarshalOString2ᚖstring(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
|
case "redirect_uri":
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("redirect_uri"))
|
||||||
|
it.RedirectURI, err = ec.unmarshalOString2ᚖstring(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return it, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) unmarshalInputOAuthRevokeInput(ctx context.Context, obj interface{}) (model.OAuthRevokeInput, error) {
|
||||||
|
var it model.OAuthRevokeInput
|
||||||
|
asMap := map[string]interface{}{}
|
||||||
|
for k, v := range obj.(map[string]interface{}) {
|
||||||
|
asMap[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range asMap {
|
||||||
|
switch k {
|
||||||
|
case "refresh_token":
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("refresh_token"))
|
||||||
|
it.RefreshToken, err = ec.unmarshalNString2string(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7921,6 +8138,11 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
|
|||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
invalids++
|
invalids++
|
||||||
}
|
}
|
||||||
|
case "revoke":
|
||||||
|
out.Values[i] = ec._Mutation_revoke(ctx, field)
|
||||||
|
if out.Values[i] == graphql.Null {
|
||||||
|
invalids++
|
||||||
|
}
|
||||||
case "_delete_user":
|
case "_delete_user":
|
||||||
out.Values[i] = ec._Mutation__delete_user(ctx, field)
|
out.Values[i] = ec._Mutation__delete_user(ctx, field)
|
||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
@@ -8290,6 +8512,10 @@ func (ec *executionContext) _VerificationRequest(ctx context.Context, sel ast.Se
|
|||||||
out.Values[i] = ec._VerificationRequest_created_at(ctx, field, obj)
|
out.Values[i] = ec._VerificationRequest_created_at(ctx, field, obj)
|
||||||
case "updated_at":
|
case "updated_at":
|
||||||
out.Values[i] = ec._VerificationRequest_updated_at(ctx, field, obj)
|
out.Values[i] = ec._VerificationRequest_updated_at(ctx, field, obj)
|
||||||
|
case "nonce":
|
||||||
|
out.Values[i] = ec._VerificationRequest_nonce(ctx, field, obj)
|
||||||
|
case "redirect_uri":
|
||||||
|
out.Values[i] = ec._VerificationRequest_redirect_uri(ctx, field, obj)
|
||||||
default:
|
default:
|
||||||
panic("unknown field " + strconv.Quote(field.Name))
|
panic("unknown field " + strconv.Quote(field.Name))
|
||||||
}
|
}
|
||||||
@@ -8700,6 +8926,11 @@ func (ec *executionContext) marshalNMeta2ᚖgithubᚗcomᚋauthorizerdevᚋautho
|
|||||||
return ec._Meta(ctx, sel, v)
|
return ec._Meta(ctx, sel, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) unmarshalNOAuthRevokeInput2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐOAuthRevokeInput(ctx context.Context, v interface{}) (model.OAuthRevokeInput, error) {
|
||||||
|
res, err := ec.unmarshalInputOAuthRevokeInput(ctx, v)
|
||||||
|
return res, graphql.ErrorOnPath(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) marshalNPagination2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐPagination(ctx context.Context, sel ast.SelectionSet, v *model.Pagination) graphql.Marshaler {
|
func (ec *executionContext) marshalNPagination2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐPagination(ctx context.Context, sel ast.SelectionSet, v *model.Pagination) graphql.Marshaler {
|
||||||
if v == nil {
|
if v == nil {
|
||||||
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
|
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
|
||||||
|
@@ -69,7 +69,9 @@ type Error struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ForgotPasswordInput struct {
|
type ForgotPasswordInput struct {
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
|
State *string `json:"state"`
|
||||||
|
RedirectURI *string `json:"redirect_uri"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LoginInput struct {
|
type LoginInput struct {
|
||||||
@@ -80,9 +82,11 @@ type LoginInput struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type MagicLinkLoginInput struct {
|
type MagicLinkLoginInput struct {
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Roles []string `json:"roles"`
|
Roles []string `json:"roles"`
|
||||||
Scope []string `json:"scope"`
|
Scope []string `json:"scope"`
|
||||||
|
State *string `json:"state"`
|
||||||
|
RedirectURI *string `json:"redirect_uri"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Meta struct {
|
type Meta struct {
|
||||||
@@ -96,6 +100,10 @@ type Meta struct {
|
|||||||
IsMagicLinkLoginEnabled bool `json:"is_magic_link_login_enabled"`
|
IsMagicLinkLoginEnabled bool `json:"is_magic_link_login_enabled"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type OAuthRevokeInput struct {
|
||||||
|
RefreshToken string `json:"refresh_token"`
|
||||||
|
}
|
||||||
|
|
||||||
type PaginatedInput struct {
|
type PaginatedInput struct {
|
||||||
Pagination *PaginationInput `json:"pagination"`
|
Pagination *PaginationInput `json:"pagination"`
|
||||||
}
|
}
|
||||||
@@ -239,13 +247,15 @@ type Users struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type VerificationRequest struct {
|
type VerificationRequest struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Identifier *string `json:"identifier"`
|
Identifier *string `json:"identifier"`
|
||||||
Token *string `json:"token"`
|
Token *string `json:"token"`
|
||||||
Email *string `json:"email"`
|
Email *string `json:"email"`
|
||||||
Expires *int64 `json:"expires"`
|
Expires *int64 `json:"expires"`
|
||||||
CreatedAt *int64 `json:"created_at"`
|
CreatedAt *int64 `json:"created_at"`
|
||||||
UpdatedAt *int64 `json:"updated_at"`
|
UpdatedAt *int64 `json:"updated_at"`
|
||||||
|
Nonce *string `json:"nonce"`
|
||||||
|
RedirectURI *string `json:"redirect_uri"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type VerificationRequests struct {
|
type VerificationRequests struct {
|
||||||
|
@@ -57,6 +57,8 @@ type VerificationRequest {
|
|||||||
expires: Int64
|
expires: Int64
|
||||||
created_at: Int64
|
created_at: Int64
|
||||||
updated_at: Int64
|
updated_at: Int64
|
||||||
|
nonce: String
|
||||||
|
redirect_uri: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type VerificationRequests {
|
type VerificationRequests {
|
||||||
@@ -229,6 +231,8 @@ input UpdateUserInput {
|
|||||||
|
|
||||||
input ForgotPasswordInput {
|
input ForgotPasswordInput {
|
||||||
email: String!
|
email: String!
|
||||||
|
state: String
|
||||||
|
redirect_uri: String
|
||||||
}
|
}
|
||||||
|
|
||||||
input ResetPasswordInput {
|
input ResetPasswordInput {
|
||||||
@@ -245,6 +249,8 @@ input MagicLinkLoginInput {
|
|||||||
email: String!
|
email: String!
|
||||||
roles: [String!]
|
roles: [String!]
|
||||||
scope: [String!]
|
scope: [String!]
|
||||||
|
state: String
|
||||||
|
redirect_uri: String
|
||||||
}
|
}
|
||||||
|
|
||||||
input SessionQueryInput {
|
input SessionQueryInput {
|
||||||
@@ -261,6 +267,10 @@ input PaginatedInput {
|
|||||||
pagination: PaginationInput
|
pagination: PaginationInput
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input OAuthRevokeInput {
|
||||||
|
refresh_token: String!
|
||||||
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
signup(params: SignUpInput!): AuthResponse!
|
signup(params: SignUpInput!): AuthResponse!
|
||||||
login(params: LoginInput!): AuthResponse!
|
login(params: LoginInput!): AuthResponse!
|
||||||
@@ -271,6 +281,7 @@ type Mutation {
|
|||||||
resend_verify_email(params: ResendVerifyEmailInput!): Response!
|
resend_verify_email(params: ResendVerifyEmailInput!): Response!
|
||||||
forgot_password(params: ForgotPasswordInput!): Response!
|
forgot_password(params: ForgotPasswordInput!): Response!
|
||||||
reset_password(params: ResetPasswordInput!): Response!
|
reset_password(params: ResetPasswordInput!): Response!
|
||||||
|
revoke(params: OAuthRevokeInput!): Response!
|
||||||
# admin only apis
|
# admin only apis
|
||||||
_delete_user(params: DeleteUserInput!): Response!
|
_delete_user(params: DeleteUserInput!): Response!
|
||||||
_update_user(params: UpdateUserInput!): User!
|
_update_user(params: UpdateUserInput!): User!
|
||||||
|
@@ -47,6 +47,10 @@ func (r *mutationResolver) ResetPassword(ctx context.Context, params model.Reset
|
|||||||
return resolvers.ResetPasswordResolver(ctx, params)
|
return resolvers.ResetPasswordResolver(ctx, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *mutationResolver) Revoke(ctx context.Context, params model.OAuthRevokeInput) (*model.Response, error) {
|
||||||
|
return resolvers.RevokeResolver(ctx, params)
|
||||||
|
}
|
||||||
|
|
||||||
func (r *mutationResolver) DeleteUser(ctx context.Context, params model.DeleteUserInput) (*model.Response, error) {
|
func (r *mutationResolver) DeleteUser(ctx context.Context, params model.DeleteUserInput) (*model.Response, error) {
|
||||||
return resolvers.DeleteUserResolver(ctx, params)
|
return resolvers.DeleteUserResolver(ctx, params)
|
||||||
}
|
}
|
||||||
@@ -105,5 +109,7 @@ func (r *Resolver) Mutation() generated.MutationResolver { return &mutationResol
|
|||||||
// Query returns generated.QueryResolver implementation.
|
// Query returns generated.QueryResolver implementation.
|
||||||
func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }
|
func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }
|
||||||
|
|
||||||
type mutationResolver struct{ *Resolver }
|
type (
|
||||||
type queryResolver struct{ *Resolver }
|
mutationResolver struct{ *Resolver }
|
||||||
|
queryResolver struct{ *Resolver }
|
||||||
|
)
|
||||||
|
@@ -1,13 +1,11 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/crypto"
|
|
||||||
"github.com/authorizerdev/authorizer/server/envstore"
|
"github.com/authorizerdev/authorizer/server/envstore"
|
||||||
"github.com/authorizerdev/authorizer/server/utils"
|
"github.com/authorizerdev/authorizer/server/utils"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@@ -29,44 +27,25 @@ func AppHandler() gin.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
state := c.Query("state")
|
redirect_uri := strings.TrimSpace(c.Query("redirect_uri"))
|
||||||
|
state := strings.TrimSpace(c.Query("state"))
|
||||||
|
scopeString := strings.TrimSpace(c.Query("scope"))
|
||||||
|
|
||||||
var stateObj State
|
var scope []string
|
||||||
|
if scopeString == "" {
|
||||||
if state == "" {
|
scope = []string{"openid", "profile", "email"}
|
||||||
stateObj.AuthorizerURL = hostname
|
|
||||||
stateObj.RedirectURL = hostname + "/app"
|
|
||||||
} else {
|
} else {
|
||||||
decodedState, err := crypto.DecryptB64(state)
|
scope = strings.Split(scopeString, " ")
|
||||||
if err != nil {
|
}
|
||||||
c.JSON(400, gin.H{"error": "[unable to decode state] invalid state"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal([]byte(decodedState), &stateObj)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(400, gin.H{"error": "[unable to parse state] invalid state"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
stateObj.AuthorizerURL = strings.TrimSuffix(stateObj.AuthorizerURL, "/")
|
|
||||||
stateObj.RedirectURL = strings.TrimSuffix(stateObj.RedirectURL, "/")
|
|
||||||
|
|
||||||
|
if redirect_uri == "" {
|
||||||
|
redirect_uri = hostname + "/app"
|
||||||
|
} else {
|
||||||
// validate redirect url with allowed origins
|
// validate redirect url with allowed origins
|
||||||
if !utils.IsValidOrigin(stateObj.RedirectURL) {
|
if !utils.IsValidOrigin(redirect_uri) {
|
||||||
c.JSON(400, gin.H{"error": "invalid redirect url"})
|
c.JSON(400, gin.H{"error": "invalid redirect url"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if stateObj.AuthorizerURL == "" {
|
|
||||||
c.JSON(400, gin.H{"error": "invalid authorizer url"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate host and domain of authorizer url
|
|
||||||
if strings.TrimSuffix(stateObj.AuthorizerURL, "/") != hostname {
|
|
||||||
c.JSON(400, gin.H{"error": "invalid host url"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// debug the request state
|
// debug the request state
|
||||||
@@ -77,9 +56,11 @@ func AppHandler() gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.HTML(http.StatusOK, "app.tmpl", gin.H{
|
c.HTML(http.StatusOK, "app.tmpl", gin.H{
|
||||||
"data": map[string]string{
|
"data": map[string]interface{}{
|
||||||
"authorizerURL": stateObj.AuthorizerURL,
|
"authorizerURL": hostname,
|
||||||
"redirectURL": stateObj.RedirectURL,
|
"redirectURL": redirect_uri,
|
||||||
|
"scope": scope,
|
||||||
|
"state": state,
|
||||||
"organizationName": envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyOrganizationName),
|
"organizationName": envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyOrganizationName),
|
||||||
"organizationLogo": envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyOrganizationLogo),
|
"organizationLogo": envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyOrganizationLogo),
|
||||||
},
|
},
|
||||||
|
@@ -2,6 +2,7 @@ package handlers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
@@ -17,6 +18,7 @@ import (
|
|||||||
// AuthorizeHandler is the handler for the /authorize route
|
// AuthorizeHandler is the handler for the /authorize route
|
||||||
// required params
|
// required params
|
||||||
// ?redirect_uri = redirect url
|
// ?redirect_uri = redirect url
|
||||||
|
// ?response_mode = to decide if result should be html or re-direct
|
||||||
// state[recommended] = to prevent CSRF attack (for authorizer its compulsory)
|
// state[recommended] = to prevent CSRF attack (for authorizer its compulsory)
|
||||||
// code_challenge = to prevent CSRF attack
|
// code_challenge = to prevent CSRF attack
|
||||||
// code_challenge_method = to prevent CSRF attack [only sh256 is supported]
|
// code_challenge_method = to prevent CSRF attack [only sh256 is supported]
|
||||||
@@ -31,62 +33,7 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||||||
scopeString := strings.TrimSpace(gc.Query("scope"))
|
scopeString := strings.TrimSpace(gc.Query("scope"))
|
||||||
clientID := strings.TrimSpace(gc.Query("client_id"))
|
clientID := strings.TrimSpace(gc.Query("client_id"))
|
||||||
template := "authorize.tmpl"
|
template := "authorize.tmpl"
|
||||||
|
responseMode := strings.TrimSpace(gc.Query("response_mode"))
|
||||||
if clientID == "" {
|
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
|
||||||
"target_origin": redirectURI,
|
|
||||||
"authorization_response": map[string]interface{}{
|
|
||||||
"type": "authorization_response",
|
|
||||||
"response": map[string]string{
|
|
||||||
"error": "client_id is required",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if clientID != envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyClientID) {
|
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
|
||||||
"target_origin": redirectURI,
|
|
||||||
"authorization_response": map[string]interface{}{
|
|
||||||
"type": "authorization_response",
|
|
||||||
"response": map[string]string{
|
|
||||||
"error": "invalid_client_id",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if redirectURI == "" {
|
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
|
||||||
"target_origin": redirectURI,
|
|
||||||
"authorization_response": map[string]interface{}{
|
|
||||||
"type": "authorization_response",
|
|
||||||
"response": map[string]string{
|
|
||||||
"error": "redirect_uri is required",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if state == "" {
|
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
|
||||||
"target_origin": redirectURI,
|
|
||||||
"authorization_response": map[string]interface{}{
|
|
||||||
"type": "authorization_response",
|
|
||||||
"response": map[string]string{
|
|
||||||
"error": "state is required",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if responseType == "" {
|
|
||||||
responseType = "token"
|
|
||||||
}
|
|
||||||
|
|
||||||
var scope []string
|
var scope []string
|
||||||
if scopeString == "" {
|
if scopeString == "" {
|
||||||
@@ -95,80 +42,171 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||||||
scope = strings.Split(scopeString, " ")
|
scope = strings.Split(scopeString, " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if responseMode == "" {
|
||||||
|
responseMode = "query"
|
||||||
|
}
|
||||||
|
|
||||||
|
if responseMode != "query" && responseMode != "web_message" {
|
||||||
|
gc.JSON(400, gin.H{"error": "invalid response mode"})
|
||||||
|
}
|
||||||
|
|
||||||
|
if redirectURI == "" {
|
||||||
|
redirectURI = "/app"
|
||||||
|
}
|
||||||
|
|
||||||
|
isQuery := responseMode == "query"
|
||||||
|
|
||||||
|
loginURL := "/app?state=" + state + "&scope=" + strings.Join(scope, " ") + "&redirect_uri=" + redirectURI
|
||||||
|
|
||||||
|
if clientID == "" {
|
||||||
|
if isQuery {
|
||||||
|
gc.Redirect(http.StatusFound, loginURL)
|
||||||
|
} else {
|
||||||
|
gc.HTML(http.StatusOK, template, gin.H{
|
||||||
|
"target_origin": redirectURI,
|
||||||
|
"authorization_response": map[string]interface{}{
|
||||||
|
"type": "authorization_response",
|
||||||
|
"response": map[string]string{
|
||||||
|
"error": "client_id is required",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if clientID != envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyClientID) {
|
||||||
|
if isQuery {
|
||||||
|
gc.Redirect(http.StatusFound, loginURL)
|
||||||
|
} else {
|
||||||
|
gc.HTML(http.StatusOK, template, gin.H{
|
||||||
|
"target_origin": redirectURI,
|
||||||
|
"authorization_response": map[string]interface{}{
|
||||||
|
"type": "authorization_response",
|
||||||
|
"response": map[string]string{
|
||||||
|
"error": "invalid_client_id",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if state == "" {
|
||||||
|
if isQuery {
|
||||||
|
gc.Redirect(http.StatusFound, loginURL)
|
||||||
|
} else {
|
||||||
|
gc.HTML(http.StatusOK, template, gin.H{
|
||||||
|
"target_origin": redirectURI,
|
||||||
|
"authorization_response": map[string]interface{}{
|
||||||
|
"type": "authorization_response",
|
||||||
|
"response": map[string]string{
|
||||||
|
"error": "state is required",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if responseType == "" {
|
||||||
|
responseType = "token"
|
||||||
|
}
|
||||||
|
|
||||||
isResponseTypeCode := responseType == "code"
|
isResponseTypeCode := responseType == "code"
|
||||||
isResponseTypeToken := responseType == "token"
|
isResponseTypeToken := responseType == "token"
|
||||||
|
|
||||||
if !isResponseTypeCode && !isResponseTypeToken {
|
if !isResponseTypeCode && !isResponseTypeToken {
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
if isQuery {
|
||||||
"target_origin": redirectURI,
|
gc.Redirect(http.StatusFound, loginURL)
|
||||||
"authorization_response": map[string]interface{}{
|
} else {
|
||||||
"type": "authorization_response",
|
gc.HTML(http.StatusOK, template, gin.H{
|
||||||
"response": map[string]string{
|
"target_origin": redirectURI,
|
||||||
"error": "response_type is invalid",
|
"authorization_response": map[string]interface{}{
|
||||||
|
"type": "authorization_response",
|
||||||
|
"response": map[string]string{
|
||||||
|
"error": "response_type is invalid",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
})
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if isResponseTypeCode {
|
if isResponseTypeCode {
|
||||||
if codeChallenge == "" {
|
if codeChallenge == "" {
|
||||||
gc.HTML(http.StatusBadRequest, template, gin.H{
|
if isQuery {
|
||||||
"target_origin": redirectURI,
|
gc.Redirect(http.StatusFound, loginURL)
|
||||||
"authorization_response": map[string]interface{}{
|
} else {
|
||||||
"type": "authorization_response",
|
gc.HTML(http.StatusBadRequest, template, gin.H{
|
||||||
"response": map[string]string{
|
"target_origin": redirectURI,
|
||||||
"error": "code_challenge is required",
|
"authorization_response": map[string]interface{}{
|
||||||
|
"type": "authorization_response",
|
||||||
|
"response": map[string]string{
|
||||||
|
"error": "code_challenge is required",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
})
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sessionToken, err := cookie.GetSession(gc)
|
sessionToken, err := cookie.GetSession(gc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
if isQuery {
|
||||||
"target_origin": redirectURI,
|
gc.Redirect(http.StatusFound, loginURL)
|
||||||
"authorization_response": map[string]interface{}{
|
} else {
|
||||||
"type": "authorization_response",
|
gc.HTML(http.StatusOK, template, gin.H{
|
||||||
"response": map[string]string{
|
"target_origin": redirectURI,
|
||||||
"error": "login_required",
|
"authorization_response": map[string]interface{}{
|
||||||
"error_description": "Login is required",
|
"type": "authorization_response",
|
||||||
|
"response": map[string]string{
|
||||||
|
"error": "login_required",
|
||||||
|
"error_description": "Login is required",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
})
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// get session from cookie
|
// get session from cookie
|
||||||
claims, err := token.ValidateBrowserSession(gc, sessionToken)
|
claims, err := token.ValidateBrowserSession(gc, sessionToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
if isQuery {
|
||||||
"target_origin": redirectURI,
|
gc.Redirect(http.StatusFound, loginURL)
|
||||||
"authorization_response": map[string]interface{}{
|
} else {
|
||||||
"type": "authorization_response",
|
gc.HTML(http.StatusOK, template, gin.H{
|
||||||
"response": map[string]string{
|
"target_origin": redirectURI,
|
||||||
"error": "login_required",
|
"authorization_response": map[string]interface{}{
|
||||||
"error_description": "Login is required",
|
"type": "authorization_response",
|
||||||
|
"response": map[string]string{
|
||||||
|
"error": "login_required",
|
||||||
|
"error_description": "Login is required",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
})
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
userID := claims.Subject
|
userID := claims.Subject
|
||||||
user, err := db.Provider.GetUserByID(userID)
|
user, err := db.Provider.GetUserByID(userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
if isQuery {
|
||||||
"target_origin": redirectURI,
|
gc.Redirect(http.StatusFound, loginURL)
|
||||||
"authorization_response": map[string]interface{}{
|
} else {
|
||||||
"type": "authorization_response",
|
gc.HTML(http.StatusOK, template, gin.H{
|
||||||
"response": map[string]string{
|
"target_origin": redirectURI,
|
||||||
"error": "signup_required",
|
"authorization_response": map[string]interface{}{
|
||||||
"error_description": "Sign up required",
|
"type": "authorization_response",
|
||||||
|
"response": map[string]string{
|
||||||
|
"error": "signup_required",
|
||||||
|
"error_description": "Sign up required",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
})
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,16 +218,20 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||||||
nonce := uuid.New().String()
|
nonce := uuid.New().String()
|
||||||
newSessionTokenData, newSessionToken, err := token.CreateSessionToken(user, nonce, claims.Roles, scope)
|
newSessionTokenData, newSessionToken, err := token.CreateSessionToken(user, nonce, claims.Roles, scope)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
if isQuery {
|
||||||
"target_origin": redirectURI,
|
gc.Redirect(http.StatusFound, loginURL)
|
||||||
"authorization_response": map[string]interface{}{
|
} else {
|
||||||
"type": "authorization_response",
|
gc.HTML(http.StatusOK, template, gin.H{
|
||||||
"response": map[string]string{
|
"target_origin": redirectURI,
|
||||||
"error": "login_required",
|
"authorization_response": map[string]interface{}{
|
||||||
"error_description": "Login is required",
|
"type": "authorization_response",
|
||||||
|
"response": map[string]string{
|
||||||
|
"error": "login_required",
|
||||||
|
"error_description": "Login is required",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
})
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,24 +256,31 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||||||
// rollover the session for security
|
// rollover the session for security
|
||||||
authToken, err := token.CreateAuthToken(gc, user, claims.Roles, scope)
|
authToken, err := token.CreateAuthToken(gc, user, claims.Roles, scope)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
if isQuery {
|
||||||
"target_origin": redirectURI,
|
gc.Redirect(http.StatusFound, loginURL)
|
||||||
"authorization_response": map[string]interface{}{
|
} else {
|
||||||
"type": "authorization_response",
|
gc.HTML(http.StatusOK, template, gin.H{
|
||||||
"response": map[string]string{
|
"target_origin": redirectURI,
|
||||||
"error": "login_required",
|
"authorization_response": map[string]interface{}{
|
||||||
"error_description": "Login is required",
|
"type": "authorization_response",
|
||||||
|
"response": map[string]string{
|
||||||
|
"error": "login_required",
|
||||||
|
"error_description": "Login is required",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
})
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sessionstore.RemoveState(sessionToken)
|
sessionstore.RemoveState(sessionToken)
|
||||||
sessionstore.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
|
sessionstore.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
|
||||||
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
||||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||||
|
|
||||||
expiresIn := int64(1800)
|
expiresIn := int64(1800)
|
||||||
|
|
||||||
|
// used of query mode
|
||||||
|
params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(expiresIn, 10) + "&state=" + state + "&id_token=" + authToken.IDToken.Token
|
||||||
|
|
||||||
res := map[string]interface{}{
|
res := map[string]interface{}{
|
||||||
"access_token": authToken.AccessToken.Token,
|
"access_token": authToken.AccessToken.Token,
|
||||||
"id_token": authToken.IDToken.Token,
|
"id_token": authToken.IDToken.Token,
|
||||||
@@ -243,29 +292,42 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||||||
|
|
||||||
if authToken.RefreshToken != nil {
|
if authToken.RefreshToken != nil {
|
||||||
res["refresh_token"] = authToken.RefreshToken.Token
|
res["refresh_token"] = authToken.RefreshToken.Token
|
||||||
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
params += "&refresh_token=" + authToken.RefreshToken.Token
|
||||||
|
sessionstore.SetState(authToken.RefreshToken.Token, authToken.FingerPrint+"@"+user.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
if isQuery {
|
||||||
"target_origin": redirectURI,
|
if strings.Contains(redirectURI, "?") {
|
||||||
"authorization_response": map[string]interface{}{
|
gc.Redirect(http.StatusFound, redirectURI+"&"+params)
|
||||||
"type": "authorization_response",
|
} else {
|
||||||
"response": res,
|
gc.Redirect(http.StatusFound, redirectURI+"?"+params)
|
||||||
},
|
}
|
||||||
})
|
} else {
|
||||||
|
gc.HTML(http.StatusOK, template, gin.H{
|
||||||
|
"target_origin": redirectURI,
|
||||||
|
"authorization_response": map[string]interface{}{
|
||||||
|
"type": "authorization_response",
|
||||||
|
"response": res,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// by default return with error
|
if isQuery {
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
gc.Redirect(http.StatusFound, loginURL)
|
||||||
"target_origin": redirectURI,
|
} else {
|
||||||
"authorization_response": map[string]interface{}{
|
// by default return with error
|
||||||
"type": "authorization_response",
|
gc.HTML(http.StatusOK, template, gin.H{
|
||||||
"response": map[string]string{
|
"target_origin": redirectURI,
|
||||||
"error": "login_required",
|
"authorization_response": map[string]interface{}{
|
||||||
"error_description": "Login is required",
|
"type": "authorization_response",
|
||||||
|
"response": map[string]string{
|
||||||
|
"error": "login_required",
|
||||||
|
"error_description": "Login is required",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,7 @@ package handlers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/cookie"
|
"github.com/authorizerdev/authorizer/server/cookie"
|
||||||
"github.com/authorizerdev/authorizer/server/crypto"
|
"github.com/authorizerdev/authorizer/server/crypto"
|
||||||
@@ -9,8 +10,10 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Handler to logout user
|
||||||
func LogoutHandler() gin.HandlerFunc {
|
func LogoutHandler() gin.HandlerFunc {
|
||||||
return func(gc *gin.Context) {
|
return func(gc *gin.Context) {
|
||||||
|
redirectURL := strings.TrimSpace(gc.Query("redirect_uri"))
|
||||||
// get fingerprint hash
|
// get fingerprint hash
|
||||||
fingerprintHash, err := cookie.GetSession(gc)
|
fingerprintHash, err := cookie.GetSession(gc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -33,8 +36,12 @@ func LogoutHandler() gin.HandlerFunc {
|
|||||||
sessionstore.RemoveState(fingerPrint)
|
sessionstore.RemoveState(fingerPrint)
|
||||||
cookie.DeleteSession(gc)
|
cookie.DeleteSession(gc)
|
||||||
|
|
||||||
gc.JSON(http.StatusOK, gin.H{
|
if redirectURL != "" {
|
||||||
"message": "Logged out successfully",
|
gc.Redirect(http.StatusFound, redirectURL)
|
||||||
})
|
} else {
|
||||||
|
gc.JSON(http.StatusOK, gin.H{
|
||||||
|
"message": "Logged out successfully",
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,6 +7,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -21,7 +22,6 @@ import (
|
|||||||
"github.com/authorizerdev/authorizer/server/utils"
|
"github.com/authorizerdev/authorizer/server/utils"
|
||||||
"github.com/coreos/go-oidc/v3/oidc"
|
"github.com/coreos/go-oidc/v3/oidc"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/google/uuid"
|
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -39,14 +39,15 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
|||||||
// contains random token, redirect url, role
|
// contains random token, redirect url, role
|
||||||
sessionSplit := strings.Split(state, "___")
|
sessionSplit := strings.Split(state, "___")
|
||||||
|
|
||||||
// TODO validate redirect url
|
if len(sessionSplit) < 3 {
|
||||||
if len(sessionSplit) < 2 {
|
|
||||||
c.JSON(400, gin.H{"error": "invalid redirect url"})
|
c.JSON(400, gin.H{"error": "invalid redirect url"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
inputRoles := strings.Split(sessionSplit[2], ",")
|
stateValue := sessionSplit[0]
|
||||||
redirectURL := sessionSplit[1]
|
redirectURL := sessionSplit[1]
|
||||||
|
inputRoles := strings.Split(sessionSplit[2], ",")
|
||||||
|
scopes := strings.Split(sessionSplit[3], ",")
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
user := models.User{}
|
user := models.User{}
|
||||||
@@ -145,17 +146,29 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO use query param
|
authToken, err := token.CreateAuthToken(c, user, inputRoles, scopes)
|
||||||
scope := []string{"openid", "email", "profile"}
|
|
||||||
nonce := uuid.New().String()
|
|
||||||
_, newSessionToken, err := token.CreateSessionToken(user, nonce, inputRoles, scope)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(500, gin.H{"error": err.Error()})
|
c.JSON(500, gin.H{"error": err.Error()})
|
||||||
}
|
}
|
||||||
|
expiresIn := int64(1800)
|
||||||
|
params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(expiresIn, 10) + "&state=" + stateValue + "&id_token=" + authToken.IDToken.Token
|
||||||
|
|
||||||
|
cookie.SetSession(c, authToken.FingerPrintHash)
|
||||||
|
sessionstore.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
|
||||||
|
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
||||||
|
|
||||||
|
if authToken.RefreshToken != nil {
|
||||||
|
params = params + `&refresh_token=` + authToken.RefreshToken.Token
|
||||||
|
sessionstore.SetState(authToken.RefreshToken.Token, authToken.FingerPrint+"@"+user.ID)
|
||||||
|
}
|
||||||
|
|
||||||
sessionstore.SetState(newSessionToken, nonce+"@"+user.ID)
|
|
||||||
cookie.SetSession(c, newSessionToken)
|
|
||||||
go utils.SaveSessionInDB(c, user.ID)
|
go utils.SaveSessionInDB(c, user.ID)
|
||||||
|
if strings.Contains(redirectURL, "?") {
|
||||||
|
redirectURL = redirectURL + "&" + params
|
||||||
|
} else {
|
||||||
|
redirectURL = redirectURL + "?" + params
|
||||||
|
}
|
||||||
|
|
||||||
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
|
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,23 +10,38 @@ import (
|
|||||||
"github.com/authorizerdev/authorizer/server/sessionstore"
|
"github.com/authorizerdev/authorizer/server/sessionstore"
|
||||||
"github.com/authorizerdev/authorizer/server/utils"
|
"github.com/authorizerdev/authorizer/server/utils"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// OAuthLoginHandler set host in the oauth state that is useful for redirecting to oauth_callback
|
// OAuthLoginHandler set host in the oauth state that is useful for redirecting to oauth_callback
|
||||||
func OAuthLoginHandler() gin.HandlerFunc {
|
func OAuthLoginHandler() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
hostname := utils.GetHost(c)
|
hostname := utils.GetHost(c)
|
||||||
redirectURL := c.Query("redirectURL")
|
redirectURI := strings.TrimSpace(c.Query("redirectURL"))
|
||||||
roles := c.Query("roles")
|
roles := strings.TrimSpace(c.Query("roles"))
|
||||||
|
state := strings.TrimSpace(c.Query("state"))
|
||||||
|
scopeString := strings.TrimSpace(c.Query("scope"))
|
||||||
|
|
||||||
if redirectURL == "" {
|
if redirectURI == "" {
|
||||||
c.JSON(400, gin.H{
|
c.JSON(400, gin.H{
|
||||||
"error": "invalid redirect url",
|
"error": "invalid redirect uri",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if state == "" {
|
||||||
|
c.JSON(400, gin.H{
|
||||||
|
"error": "invalid state",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var scope []string
|
||||||
|
if scopeString == "" {
|
||||||
|
scope = []string{"openid", "profile", "email"}
|
||||||
|
} else {
|
||||||
|
scope = strings.Split(scopeString, " ")
|
||||||
|
}
|
||||||
|
|
||||||
if roles != "" {
|
if roles != "" {
|
||||||
// validate role
|
// validate role
|
||||||
rolesSplit := strings.Split(roles, ",")
|
rolesSplit := strings.Split(roles, ",")
|
||||||
@@ -43,8 +58,7 @@ func OAuthLoginHandler() gin.HandlerFunc {
|
|||||||
roles = strings.Join(envstore.EnvStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyDefaultRoles), ",")
|
roles = strings.Join(envstore.EnvStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyDefaultRoles), ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
uuid := uuid.New()
|
oauthStateString := state + "___" + redirectURI + "___" + roles + "___" + strings.Join(scope, ",")
|
||||||
oauthStateString := uuid.String() + "___" + redirectURL + "___" + roles
|
|
||||||
|
|
||||||
provider := c.Param("oauth_provider")
|
provider := c.Param("oauth_provider")
|
||||||
isProviderConfigured := true
|
isProviderConfigured := true
|
||||||
|
50
server/handlers/revoke.go
Normal file
50
server/handlers/revoke.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
|
"github.com/authorizerdev/authorizer/server/envstore"
|
||||||
|
"github.com/authorizerdev/authorizer/server/sessionstore"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Revoke handler to revoke refresh token
|
||||||
|
func RevokeHandler() gin.HandlerFunc {
|
||||||
|
return func(gc *gin.Context) {
|
||||||
|
var reqBody map[string]string
|
||||||
|
if err := gc.BindJSON(&reqBody); err != nil {
|
||||||
|
gc.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"error": "error_binding_json",
|
||||||
|
"error_description": err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// get fingerprint hash
|
||||||
|
refreshToken := strings.TrimSpace(reqBody["refresh_token"])
|
||||||
|
clientID := strings.TrimSpace(reqBody["client_id"])
|
||||||
|
|
||||||
|
if clientID == "" {
|
||||||
|
gc.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"error": "client_id_required",
|
||||||
|
"error_description": "The client id is required",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if clientID != envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyClientID) {
|
||||||
|
gc.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"error": "invalid_client_id",
|
||||||
|
"error_description": "The client id is invalid",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionstore.RemoveState(refreshToken)
|
||||||
|
|
||||||
|
gc.JSON(http.StatusOK, gin.H{
|
||||||
|
"message": "Token revoked successfully",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@@ -15,6 +15,8 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TokenHandler to handle /oauth/token requests
|
||||||
|
// grant type required
|
||||||
func TokenHandler() gin.HandlerFunc {
|
func TokenHandler() gin.HandlerFunc {
|
||||||
return func(gc *gin.Context) {
|
return func(gc *gin.Context) {
|
||||||
var reqBody map[string]string
|
var reqBody map[string]string
|
||||||
@@ -29,6 +31,22 @@ func TokenHandler() gin.HandlerFunc {
|
|||||||
codeVerifier := strings.TrimSpace(reqBody["code_verifier"])
|
codeVerifier := strings.TrimSpace(reqBody["code_verifier"])
|
||||||
code := strings.TrimSpace(reqBody["code"])
|
code := strings.TrimSpace(reqBody["code"])
|
||||||
clientID := strings.TrimSpace(reqBody["client_id"])
|
clientID := strings.TrimSpace(reqBody["client_id"])
|
||||||
|
grantType := strings.TrimSpace(reqBody["grant_type"])
|
||||||
|
refreshToken := strings.TrimSpace(reqBody["refresh_token"])
|
||||||
|
|
||||||
|
if grantType == "" {
|
||||||
|
grantType = "authorization_code"
|
||||||
|
}
|
||||||
|
|
||||||
|
isRefreshTokenGrant := grantType == "refresh_token"
|
||||||
|
isAuthorizationCodeGrant := grantType == "authorization_code"
|
||||||
|
|
||||||
|
if !isRefreshTokenGrant && !isAuthorizationCodeGrant {
|
||||||
|
gc.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"error": "invalid_grant_type",
|
||||||
|
"error_description": "grant_type is invalid",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if clientID == "" {
|
if clientID == "" {
|
||||||
gc.JSON(http.StatusBadRequest, gin.H{
|
gc.JSON(http.StatusBadRequest, gin.H{
|
||||||
@@ -46,58 +64,95 @@ func TokenHandler() gin.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if codeVerifier == "" {
|
var userID string
|
||||||
gc.JSON(http.StatusBadRequest, gin.H{
|
var roles, scope []string
|
||||||
"error": "invalid_code_verifier",
|
if isAuthorizationCodeGrant {
|
||||||
"error_description": "The code verifier is required",
|
|
||||||
})
|
if codeVerifier == "" {
|
||||||
return
|
gc.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"error": "invalid_code_verifier",
|
||||||
|
"error_description": "The code verifier is required",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if code == "" {
|
||||||
|
gc.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"error": "invalid_code",
|
||||||
|
"error_description": "The code is required",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := sha256.New()
|
||||||
|
hash.Write([]byte(codeVerifier))
|
||||||
|
encryptedCode := strings.ReplaceAll(base64.URLEncoding.EncodeToString(hash.Sum(nil)), "+", "-")
|
||||||
|
encryptedCode = strings.ReplaceAll(encryptedCode, "/", "_")
|
||||||
|
encryptedCode = strings.ReplaceAll(encryptedCode, "=", "")
|
||||||
|
sessionData := sessionstore.GetState(encryptedCode)
|
||||||
|
if sessionData == "" {
|
||||||
|
gc.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"error": "invalid_code_verifier",
|
||||||
|
"error_description": "The code verifier is invalid",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// split session data
|
||||||
|
// it contains code@sessiontoken
|
||||||
|
sessionDataSplit := strings.Split(sessionData, "@")
|
||||||
|
|
||||||
|
if sessionDataSplit[0] != code {
|
||||||
|
gc.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"error": "invalid_code_verifier",
|
||||||
|
"error_description": "The code verifier is invalid",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// rollover the session for security
|
||||||
|
sessionstore.RemoveState(sessionDataSplit[1])
|
||||||
|
// validate session
|
||||||
|
claims, err := token.ValidateBrowserSession(gc, sessionDataSplit[1])
|
||||||
|
if err != nil {
|
||||||
|
gc.JSON(http.StatusUnauthorized, gin.H{
|
||||||
|
"error": "unauthorized",
|
||||||
|
"error_description": "Invalid session data",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
userID = claims.Subject
|
||||||
|
roles = claims.Roles
|
||||||
|
scope = claims.Scope
|
||||||
|
} else {
|
||||||
|
// validate refresh token
|
||||||
|
if refreshToken == "" {
|
||||||
|
gc.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"error": "invalid_refresh_token",
|
||||||
|
"error_description": "The refresh token is invalid",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
claims, err := token.ValidateRefreshToken(gc, refreshToken)
|
||||||
|
if err != nil {
|
||||||
|
gc.JSON(http.StatusUnauthorized, gin.H{
|
||||||
|
"error": "unauthorized",
|
||||||
|
"error_description": err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
userID = claims["sub"].(string)
|
||||||
|
rolesInterface := claims["roles"].([]interface{})
|
||||||
|
scopeInterface := claims["scope"].([]interface{})
|
||||||
|
for _, v := range rolesInterface {
|
||||||
|
roles = append(roles, v.(string))
|
||||||
|
}
|
||||||
|
for _, v := range scopeInterface {
|
||||||
|
scope = append(scope, v.(string))
|
||||||
|
}
|
||||||
|
// remove older refresh token and rotate it for security
|
||||||
|
sessionstore.RemoveState(refreshToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
if code == "" {
|
|
||||||
gc.JSON(http.StatusBadRequest, gin.H{
|
|
||||||
"error": "invalid_code",
|
|
||||||
"error_description": "The code is required",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
hash := sha256.New()
|
|
||||||
hash.Write([]byte(codeVerifier))
|
|
||||||
encryptedCode := strings.ReplaceAll(base64.URLEncoding.EncodeToString(hash.Sum(nil)), "+", "-")
|
|
||||||
encryptedCode = strings.ReplaceAll(encryptedCode, "/", "_")
|
|
||||||
encryptedCode = strings.ReplaceAll(encryptedCode, "=", "")
|
|
||||||
sessionData := sessionstore.GetState(encryptedCode)
|
|
||||||
if sessionData == "" {
|
|
||||||
gc.JSON(http.StatusBadRequest, gin.H{
|
|
||||||
"error": "invalid_code_verifier",
|
|
||||||
"error_description": "The code verifier is invalid",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// split session data
|
|
||||||
// it contains code@sessiontoken
|
|
||||||
sessionDataSplit := strings.Split(sessionData, "@")
|
|
||||||
|
|
||||||
if sessionDataSplit[0] != code {
|
|
||||||
gc.JSON(http.StatusBadRequest, gin.H{
|
|
||||||
"error": "invalid_code_verifier",
|
|
||||||
"error_description": "The code verifier is invalid",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate session
|
|
||||||
claims, err := token.ValidateBrowserSession(gc, sessionDataSplit[1])
|
|
||||||
if err != nil {
|
|
||||||
gc.JSON(http.StatusUnauthorized, gin.H{
|
|
||||||
"error": "unauthorized",
|
|
||||||
"error_description": "Invalid session data",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
userID := claims.Subject
|
|
||||||
user, err := db.Provider.GetUserByID(userID)
|
user, err := db.Provider.GetUserByID(userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gc.JSON(http.StatusUnauthorized, gin.H{
|
gc.JSON(http.StatusUnauthorized, gin.H{
|
||||||
@@ -106,9 +161,8 @@ func TokenHandler() gin.HandlerFunc {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// rollover the session for security
|
|
||||||
sessionstore.RemoveState(sessionDataSplit[1])
|
authToken, err := token.CreateAuthToken(gc, user, roles, scope)
|
||||||
authToken, err := token.CreateAuthToken(gc, user, claims.Roles, claims.Scope)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gc.JSON(http.StatusUnauthorized, gin.H{
|
gc.JSON(http.StatusUnauthorized, gin.H{
|
||||||
"error": "unauthorized",
|
"error": "unauthorized",
|
||||||
@@ -124,13 +178,14 @@ func TokenHandler() gin.HandlerFunc {
|
|||||||
res := map[string]interface{}{
|
res := map[string]interface{}{
|
||||||
"access_token": authToken.AccessToken.Token,
|
"access_token": authToken.AccessToken.Token,
|
||||||
"id_token": authToken.IDToken.Token,
|
"id_token": authToken.IDToken.Token,
|
||||||
"scope": claims.Scope,
|
"scope": scope,
|
||||||
|
"roles": roles,
|
||||||
"expires_in": expiresIn,
|
"expires_in": expiresIn,
|
||||||
}
|
}
|
||||||
|
|
||||||
if authToken.RefreshToken != nil {
|
if authToken.RefreshToken != nil {
|
||||||
res["refresh_token"] = authToken.RefreshToken.Token
|
res["refresh_token"] = authToken.RefreshToken.Token
|
||||||
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
sessionstore.SetState(authToken.RefreshToken.Token, authToken.FingerPrint+"@"+user.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
gc.JSON(http.StatusOK, res)
|
gc.JSON(http.StatusOK, res)
|
||||||
|
@@ -2,6 +2,7 @@ package handlers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -11,7 +12,6 @@ import (
|
|||||||
"github.com/authorizerdev/authorizer/server/token"
|
"github.com/authorizerdev/authorizer/server/token"
|
||||||
"github.com/authorizerdev/authorizer/server/utils"
|
"github.com/authorizerdev/authorizer/server/utils"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// VerifyEmailHandler handles the verify email route.
|
// VerifyEmailHandler handles the verify email route.
|
||||||
@@ -19,7 +19,7 @@ import (
|
|||||||
func VerifyEmailHandler() gin.HandlerFunc {
|
func VerifyEmailHandler() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
errorRes := gin.H{
|
errorRes := gin.H{
|
||||||
"error": "invalid token",
|
"error": "invalid_token",
|
||||||
}
|
}
|
||||||
tokenInQuery := c.Query("token")
|
tokenInQuery := c.Query("token")
|
||||||
if tokenInQuery == "" {
|
if tokenInQuery == "" {
|
||||||
@@ -29,30 +29,24 @@ func VerifyEmailHandler() gin.HandlerFunc {
|
|||||||
|
|
||||||
verificationRequest, err := db.Provider.GetVerificationRequestByToken(tokenInQuery)
|
verificationRequest, err := db.Provider.GetVerificationRequestByToken(tokenInQuery)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
errorRes["error_description"] = err.Error()
|
||||||
c.JSON(400, errorRes)
|
c.JSON(400, errorRes)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify if token exists in db
|
// verify if token exists in db
|
||||||
hostname := utils.GetHost(c)
|
hostname := utils.GetHost(c)
|
||||||
encryptedNonce, err := utils.EncryptNonce(verificationRequest.Nonce)
|
claim, err := token.ParseJWTToken(tokenInQuery, hostname, verificationRequest.Nonce, verificationRequest.Email)
|
||||||
if err != nil {
|
|
||||||
c.JSON(400, gin.H{
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
claim, err := token.ParseJWTToken(tokenInQuery, hostname, encryptedNonce, verificationRequest.Email)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
errorRes["error_description"] = err.Error()
|
||||||
c.JSON(400, errorRes)
|
c.JSON(400, errorRes)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := db.Provider.GetUserByEmail(claim["sub"].(string))
|
user, err := db.Provider.GetUserByEmail(claim["sub"].(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(400, gin.H{
|
errorRes["error_description"] = err.Error()
|
||||||
"message": err.Error(),
|
c.JSON(400, errorRes)
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,21 +59,53 @@ func VerifyEmailHandler() gin.HandlerFunc {
|
|||||||
// delete from verification table
|
// delete from verification table
|
||||||
db.Provider.DeleteVerificationRequest(verificationRequest)
|
db.Provider.DeleteVerificationRequest(verificationRequest)
|
||||||
|
|
||||||
roles := strings.Split(user.Roles, ",")
|
state := strings.TrimSpace(c.Query("state"))
|
||||||
scope := []string{"openid", "email", "profile"}
|
redirectURL := strings.TrimSpace(c.Query("redirect_uri"))
|
||||||
nonce := uuid.New().String()
|
rolesString := strings.TrimSpace(c.Query("roles"))
|
||||||
_, authToken, err := token.CreateSessionToken(user, nonce, roles, scope)
|
var roles []string
|
||||||
|
if rolesString == "" {
|
||||||
|
roles = strings.Split(user.Roles, ",")
|
||||||
|
} else {
|
||||||
|
roles = strings.Split(rolesString, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
scopeString := strings.TrimSpace(c.Query("scope"))
|
||||||
|
var scope []string
|
||||||
|
if scopeString == "" {
|
||||||
|
scope = []string{"openid", "email", "profile"}
|
||||||
|
} else {
|
||||||
|
scope = strings.Split(scopeString, " ")
|
||||||
|
}
|
||||||
|
authToken, err := token.CreateAuthToken(c, user, roles, scope)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(400, gin.H{
|
errorRes["error_description"] = err.Error()
|
||||||
"message": err.Error(),
|
c.JSON(500, errorRes)
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sessionstore.SetState(authToken, nonce+"@"+user.ID)
|
expiresIn := int64(1800)
|
||||||
cookie.SetSession(c, authToken)
|
params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(expiresIn, 10) + "&state=" + state + "&id_token=" + authToken.IDToken.Token
|
||||||
|
|
||||||
|
cookie.SetSession(c, authToken.FingerPrintHash)
|
||||||
|
sessionstore.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
|
||||||
|
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
||||||
|
|
||||||
|
if authToken.RefreshToken != nil {
|
||||||
|
params = params + `&refresh_token=${refresh_token}`
|
||||||
|
sessionstore.SetState(authToken.RefreshToken.Token, authToken.FingerPrint+"@"+user.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if redirectURL == "" {
|
||||||
|
redirectURL = claim["redirect_uri"].(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(redirectURL, "?") {
|
||||||
|
redirectURL = redirectURL + "&" + params
|
||||||
|
} else {
|
||||||
|
redirectURL = redirectURL + "?" + params
|
||||||
|
}
|
||||||
|
|
||||||
go utils.SaveSessionInDB(c, user.ID)
|
go utils.SaveSessionInDB(c, user.ID)
|
||||||
|
|
||||||
c.Redirect(http.StatusTemporaryRedirect, claim["redirect_url"].(string))
|
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -39,20 +39,26 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
|
|||||||
}
|
}
|
||||||
|
|
||||||
hostname := utils.GetHost(gc)
|
hostname := utils.GetHost(gc)
|
||||||
nonce, nonceHash, err := utils.GenerateNonce()
|
_, nonceHash, err := utils.GenerateNonce()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
verificationToken, err := token.CreateVerificationToken(params.Email, constants.VerificationTypeForgotPassword, hostname, nonceHash)
|
redirectURL := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAppURL)
|
||||||
|
if params.RedirectURI != nil {
|
||||||
|
redirectURL = *params.RedirectURI
|
||||||
|
}
|
||||||
|
|
||||||
|
verificationToken, err := token.CreateVerificationToken(params.Email, constants.VerificationTypeForgotPassword, hostname, nonceHash, redirectURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(`error generating token`, err)
|
log.Println(`error generating token`, err)
|
||||||
}
|
}
|
||||||
db.Provider.AddVerificationRequest(models.VerificationRequest{
|
db.Provider.AddVerificationRequest(models.VerificationRequest{
|
||||||
Token: verificationToken,
|
Token: verificationToken,
|
||||||
Identifier: constants.VerificationTypeForgotPassword,
|
Identifier: constants.VerificationTypeForgotPassword,
|
||||||
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||||
Email: params.Email,
|
Email: params.Email,
|
||||||
Nonce: nonce,
|
Nonce: nonceHash,
|
||||||
|
RedirectURI: redirectURL,
|
||||||
})
|
})
|
||||||
|
|
||||||
// exec it as go routin so that we can reduce the api latency
|
// exec it as go routin so that we can reduce the api latency
|
||||||
|
@@ -69,8 +69,6 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
|
|||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
|
||||||
|
|
||||||
expiresIn := int64(1800)
|
expiresIn := int64(1800)
|
||||||
res = &model.AuthResponse{
|
res = &model.AuthResponse{
|
||||||
Message: `Logged in successfully`,
|
Message: `Logged in successfully`,
|
||||||
@@ -80,12 +78,13 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
|
|||||||
User: user.AsAPIUser(),
|
User: user.AsAPIUser(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||||
sessionstore.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
|
sessionstore.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
|
||||||
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
||||||
|
|
||||||
if authToken.RefreshToken != nil {
|
if authToken.RefreshToken != nil {
|
||||||
res.RefreshToken = &authToken.RefreshToken.Token
|
res.RefreshToken = &authToken.RefreshToken.Token
|
||||||
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
sessionstore.SetState(authToken.RefreshToken.Token, authToken.FingerPrint+"@"+user.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
go utils.SaveSessionInDB(gc, user.ID)
|
go utils.SaveSessionInDB(gc, user.ID)
|
||||||
|
@@ -68,6 +68,9 @@ func MagicLinkLoginResolver(ctx context.Context, params model.MagicLinkLoginInpu
|
|||||||
// Need to modify roles in this case
|
// Need to modify roles in this case
|
||||||
|
|
||||||
// find the unassigned roles
|
// find the unassigned roles
|
||||||
|
if len(params.Roles) <= 0 {
|
||||||
|
inputRoles = envstore.EnvStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyDefaultRoles)
|
||||||
|
}
|
||||||
existingRoles := strings.Split(existingUser.Roles, ",")
|
existingRoles := strings.Split(existingUser.Roles, ",")
|
||||||
unasignedRoles := []string{}
|
unasignedRoles := []string{}
|
||||||
for _, ir := range inputRoles {
|
for _, ir := range inputRoles {
|
||||||
@@ -109,24 +112,46 @@ func MagicLinkLoginResolver(ctx context.Context, params model.MagicLinkLoginInpu
|
|||||||
hostname := utils.GetHost(gc)
|
hostname := utils.GetHost(gc)
|
||||||
if !envstore.EnvStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification) {
|
if !envstore.EnvStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification) {
|
||||||
// insert verification request
|
// insert verification request
|
||||||
nonce, nonceHash, err := utils.GenerateNonce()
|
_, nonceHash, err := utils.GenerateNonce()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
redirectURLParams := "&roles=" + strings.Join(inputRoles, ",")
|
||||||
|
if params.State != nil {
|
||||||
|
redirectURLParams = redirectURLParams + "&state=" + *params.State
|
||||||
|
}
|
||||||
|
if params.Scope != nil && len(params.Scope) > 0 {
|
||||||
|
redirectURLParams = redirectURLParams + "&scope=" + strings.Join(params.Scope, " ")
|
||||||
|
}
|
||||||
|
redirectURL := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAppURL)
|
||||||
|
if params.RedirectURI != nil {
|
||||||
|
redirectURL = *params.RedirectURI
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(redirectURL, "?") {
|
||||||
|
redirectURL = redirectURL + "&" + redirectURLParams
|
||||||
|
} else {
|
||||||
|
redirectURL = redirectURL + "?" + redirectURLParams
|
||||||
|
}
|
||||||
|
|
||||||
verificationType := constants.VerificationTypeMagicLinkLogin
|
verificationType := constants.VerificationTypeMagicLinkLogin
|
||||||
verificationToken, err := token.CreateVerificationToken(params.Email, verificationType, hostname, nonceHash)
|
verificationToken, err := token.CreateVerificationToken(params.Email, verificationType, hostname, nonceHash, redirectURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(`error generating token`, err)
|
log.Println(`error generating token`, err)
|
||||||
}
|
}
|
||||||
db.Provider.AddVerificationRequest(models.VerificationRequest{
|
_, err = db.Provider.AddVerificationRequest(models.VerificationRequest{
|
||||||
Token: verificationToken,
|
Token: verificationToken,
|
||||||
Identifier: verificationType,
|
Identifier: verificationType,
|
||||||
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||||
Email: params.Email,
|
Email: params.Email,
|
||||||
Nonce: nonce,
|
Nonce: nonceHash,
|
||||||
|
RedirectURI: redirectURL,
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
// exec it as go routin so that we can reduce the api latency
|
// exec it as go routing so that we can reduce the api latency
|
||||||
go email.SendVerificationMail(params.Email, verificationToken, hostname)
|
go email.SendVerificationMail(params.Email, verificationToken, hostname)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -44,20 +44,22 @@ func ResendVerifyEmailResolver(ctx context.Context, params model.ResendVerifyEma
|
|||||||
}
|
}
|
||||||
|
|
||||||
hostname := utils.GetHost(gc)
|
hostname := utils.GetHost(gc)
|
||||||
nonce, nonceHash, err := utils.GenerateNonce()
|
_, nonceHash, err := utils.GenerateNonce()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
verificationToken, err := token.CreateVerificationToken(params.Email, params.Identifier, hostname, nonceHash)
|
|
||||||
|
verificationToken, err := token.CreateVerificationToken(params.Email, params.Identifier, hostname, nonceHash, verificationRequest.RedirectURI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(`error generating token`, err)
|
log.Println(`error generating token`, err)
|
||||||
}
|
}
|
||||||
db.Provider.AddVerificationRequest(models.VerificationRequest{
|
db.Provider.AddVerificationRequest(models.VerificationRequest{
|
||||||
Token: verificationToken,
|
Token: verificationToken,
|
||||||
Identifier: params.Identifier,
|
Identifier: params.Identifier,
|
||||||
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||||
Email: params.Email,
|
Email: params.Email,
|
||||||
Nonce: nonce,
|
Nonce: nonceHash,
|
||||||
|
RedirectURI: verificationRequest.RedirectURI,
|
||||||
})
|
})
|
||||||
|
|
||||||
// exec it as go routin so that we can reduce the api latency
|
// exec it as go routin so that we can reduce the api latency
|
||||||
|
@@ -37,11 +37,7 @@ func ResetPasswordResolver(ctx context.Context, params model.ResetPasswordInput)
|
|||||||
|
|
||||||
// verify if token exists in db
|
// verify if token exists in db
|
||||||
hostname := utils.GetHost(gc)
|
hostname := utils.GetHost(gc)
|
||||||
encryptedNonce, err := utils.EncryptNonce(verificationRequest.Nonce)
|
claim, err := token.ParseJWTToken(params.Token, hostname, verificationRequest.Nonce, verificationRequest.Email)
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
claim, err := token.ParseJWTToken(params.Token, hostname, encryptedNonce, verificationRequest.Email)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, fmt.Errorf(`invalid token`)
|
return res, fmt.Errorf(`invalid token`)
|
||||||
}
|
}
|
||||||
|
16
server/resolvers/revoke.go
Normal file
16
server/resolvers/revoke.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package resolvers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
|
"github.com/authorizerdev/authorizer/server/sessionstore"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RevokeResolver resolver to revoke refresh token
|
||||||
|
func RevokeResolver(ctx context.Context, params model.OAuthRevokeInput) (*model.Response, error) {
|
||||||
|
sessionstore.RemoveState(params.RefreshToken)
|
||||||
|
return &model.Response{
|
||||||
|
Message: "Token revoked",
|
||||||
|
}, nil
|
||||||
|
}
|
@@ -80,7 +80,7 @@ func SessionResolver(ctx context.Context, params *model.SessionQueryInput) (*mod
|
|||||||
|
|
||||||
if authToken.RefreshToken != nil {
|
if authToken.RefreshToken != nil {
|
||||||
res.RefreshToken = &authToken.RefreshToken.Token
|
res.RefreshToken = &authToken.RefreshToken.Token
|
||||||
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
sessionstore.SetState(authToken.RefreshToken.Token, authToken.FingerPrint+"@"+user.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
|
@@ -123,21 +123,23 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR
|
|||||||
hostname := utils.GetHost(gc)
|
hostname := utils.GetHost(gc)
|
||||||
if !envstore.EnvStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification) {
|
if !envstore.EnvStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification) {
|
||||||
// insert verification request
|
// insert verification request
|
||||||
nonce, nonceHash, err := utils.GenerateNonce()
|
_, nonceHash, err := utils.GenerateNonce()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
verificationType := constants.VerificationTypeBasicAuthSignup
|
verificationType := constants.VerificationTypeBasicAuthSignup
|
||||||
verificationToken, err := token.CreateVerificationToken(params.Email, verificationType, hostname, nonceHash)
|
redirectURL := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAppURL)
|
||||||
|
verificationToken, err := token.CreateVerificationToken(params.Email, verificationType, hostname, nonceHash, redirectURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
db.Provider.AddVerificationRequest(models.VerificationRequest{
|
db.Provider.AddVerificationRequest(models.VerificationRequest{
|
||||||
Token: verificationToken,
|
Token: verificationToken,
|
||||||
Identifier: verificationType,
|
Identifier: verificationType,
|
||||||
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||||
Email: params.Email,
|
Email: params.Email,
|
||||||
Nonce: nonce,
|
Nonce: nonceHash,
|
||||||
|
RedirectURI: redirectURL,
|
||||||
})
|
})
|
||||||
|
|
||||||
// exec it as go routin so that we can reduce the api latency
|
// exec it as go routin so that we can reduce the api latency
|
||||||
|
@@ -129,21 +129,23 @@ func UpdateProfileResolver(ctx context.Context, params model.UpdateProfileInput)
|
|||||||
user.EmailVerifiedAt = nil
|
user.EmailVerifiedAt = nil
|
||||||
hasEmailChanged = true
|
hasEmailChanged = true
|
||||||
// insert verification request
|
// insert verification request
|
||||||
nonce, nonceHash, err := utils.GenerateNonce()
|
_, nonceHash, err := utils.GenerateNonce()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
verificationType := constants.VerificationTypeUpdateEmail
|
verificationType := constants.VerificationTypeUpdateEmail
|
||||||
verificationToken, err := token.CreateVerificationToken(newEmail, verificationType, hostname, nonceHash)
|
redirectURL := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAppURL)
|
||||||
|
verificationToken, err := token.CreateVerificationToken(newEmail, verificationType, hostname, nonceHash, redirectURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(`error generating token`, err)
|
log.Println(`error generating token`, err)
|
||||||
}
|
}
|
||||||
db.Provider.AddVerificationRequest(models.VerificationRequest{
|
db.Provider.AddVerificationRequest(models.VerificationRequest{
|
||||||
Token: verificationToken,
|
Token: verificationToken,
|
||||||
Identifier: verificationType,
|
Identifier: verificationType,
|
||||||
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||||
Email: newEmail,
|
Email: newEmail,
|
||||||
Nonce: nonce,
|
Nonce: nonceHash,
|
||||||
|
RedirectURI: redirectURL,
|
||||||
})
|
})
|
||||||
|
|
||||||
// exec it as go routin so that we can reduce the api latency
|
// exec it as go routin so that we can reduce the api latency
|
||||||
|
@@ -101,21 +101,23 @@ func UpdateUserResolver(ctx context.Context, params model.UpdateUserInput) (*mod
|
|||||||
user.Email = newEmail
|
user.Email = newEmail
|
||||||
user.EmailVerifiedAt = nil
|
user.EmailVerifiedAt = nil
|
||||||
// insert verification request
|
// insert verification request
|
||||||
nonce, nonceHash, err := utils.GenerateNonce()
|
_, nonceHash, err := utils.GenerateNonce()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
verificationType := constants.VerificationTypeUpdateEmail
|
verificationType := constants.VerificationTypeUpdateEmail
|
||||||
verificationToken, err := token.CreateVerificationToken(newEmail, verificationType, hostname, nonceHash)
|
redirectURL := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAppURL)
|
||||||
|
verificationToken, err := token.CreateVerificationToken(newEmail, verificationType, hostname, nonceHash, redirectURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(`error generating token`, err)
|
log.Println(`error generating token`, err)
|
||||||
}
|
}
|
||||||
db.Provider.AddVerificationRequest(models.VerificationRequest{
|
db.Provider.AddVerificationRequest(models.VerificationRequest{
|
||||||
Token: verificationToken,
|
Token: verificationToken,
|
||||||
Identifier: verificationType,
|
Identifier: verificationType,
|
||||||
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||||
Email: newEmail,
|
Email: newEmail,
|
||||||
Nonce: nonce,
|
Nonce: nonceHash,
|
||||||
|
RedirectURI: redirectURL,
|
||||||
})
|
})
|
||||||
|
|
||||||
// exec it as go routin so that we can reduce the api latency
|
// exec it as go routin so that we can reduce the api latency
|
||||||
|
@@ -29,11 +29,7 @@ func VerifyEmailResolver(ctx context.Context, params model.VerifyEmailInput) (*m
|
|||||||
|
|
||||||
// verify if token exists in db
|
// verify if token exists in db
|
||||||
hostname := utils.GetHost(gc)
|
hostname := utils.GetHost(gc)
|
||||||
encryptedNonce, err := utils.EncryptNonce(verificationRequest.Nonce)
|
claim, err := token.ParseJWTToken(params.Token, hostname, verificationRequest.Nonce, verificationRequest.Email)
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
claim, err := token.ParseJWTToken(params.Token, hostname, encryptedNonce, verificationRequest.Email)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, fmt.Errorf(`invalid token: %s`, err.Error())
|
return res, fmt.Errorf(`invalid token: %s`, err.Error())
|
||||||
}
|
}
|
||||||
|
@@ -27,6 +27,7 @@ func InitRouter() *gin.Engine {
|
|||||||
router.GET("/userinfo", handlers.UserInfoHandler())
|
router.GET("/userinfo", handlers.UserInfoHandler())
|
||||||
router.GET("/logout", handlers.LogoutHandler())
|
router.GET("/logout", handlers.LogoutHandler())
|
||||||
router.POST("/oauth/token", handlers.TokenHandler())
|
router.POST("/oauth/token", handlers.TokenHandler())
|
||||||
|
router.POST("/oauth/revoke", handlers.RevokeHandler())
|
||||||
|
|
||||||
router.LoadHTMLGlob("templates/*")
|
router.LoadHTMLGlob("templates/*")
|
||||||
// login page app related routes.
|
// login page app related routes.
|
||||||
|
@@ -91,7 +91,7 @@ func CreateAuthToken(gc *gin.Context, user models.User, roles, scope []string) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if utils.StringSliceContains(scope, "offline_access") {
|
if utils.StringSliceContains(scope, "offline_access") {
|
||||||
refreshToken, refreshTokenExpiresAt, err := CreateRefreshToken(user, roles, hostname, nonce)
|
refreshToken, refreshTokenExpiresAt, err := CreateRefreshToken(user, roles, scope, hostname, nonce)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -103,7 +103,7 @@ func CreateAuthToken(gc *gin.Context, user models.User, roles, scope []string) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateRefreshToken util to create JWT token
|
// CreateRefreshToken util to create JWT token
|
||||||
func CreateRefreshToken(user models.User, roles []string, hostname, nonce string) (string, int64, error) {
|
func CreateRefreshToken(user models.User, roles, scopes []string, hostname, nonce string) (string, int64, error) {
|
||||||
// expires in 1 year
|
// expires in 1 year
|
||||||
expiryBound := time.Hour * 8760
|
expiryBound := time.Hour * 8760
|
||||||
expiresAt := time.Now().Add(expiryBound).Unix()
|
expiresAt := time.Now().Add(expiryBound).Unix()
|
||||||
@@ -115,6 +115,7 @@ func CreateRefreshToken(user models.User, roles []string, hostname, nonce string
|
|||||||
"iat": time.Now().Unix(),
|
"iat": time.Now().Unix(),
|
||||||
"token_type": constants.TokenTypeRefreshToken,
|
"token_type": constants.TokenTypeRefreshToken,
|
||||||
"roles": roles,
|
"roles": roles,
|
||||||
|
"scope": scopes,
|
||||||
"nonce": nonce,
|
"nonce": nonce,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,6 +199,36 @@ func ValidateAccessToken(gc *gin.Context, accessToken string) (map[string]interf
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function to validate refreshToken
|
||||||
|
func ValidateRefreshToken(gc *gin.Context, refreshToken string) (map[string]interface{}, error) {
|
||||||
|
var res map[string]interface{}
|
||||||
|
|
||||||
|
if refreshToken == "" {
|
||||||
|
return res, fmt.Errorf(`unauthorized`)
|
||||||
|
}
|
||||||
|
|
||||||
|
savedSession := sessionstore.GetState(refreshToken)
|
||||||
|
if savedSession == "" {
|
||||||
|
return res, fmt.Errorf(`unauthorized`)
|
||||||
|
}
|
||||||
|
|
||||||
|
savedSessionSplit := strings.Split(savedSession, "@")
|
||||||
|
nonce := savedSessionSplit[0]
|
||||||
|
userID := savedSessionSplit[1]
|
||||||
|
|
||||||
|
hostname := utils.GetHost(gc)
|
||||||
|
res, err := ParseJWTToken(refreshToken, hostname, nonce, userID)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if res["token_type"] != constants.TokenTypeRefreshToken {
|
||||||
|
return res, fmt.Errorf(`unauthorized: invalid token type`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
func ValidateBrowserSession(gc *gin.Context, encryptedSession string) (*SessionData, error) {
|
func ValidateBrowserSession(gc *gin.Context, encryptedSession string) (*SessionData, error) {
|
||||||
if encryptedSession == "" {
|
if encryptedSession == "" {
|
||||||
return nil, fmt.Errorf(`unauthorized`)
|
return nil, fmt.Errorf(`unauthorized`)
|
||||||
|
@@ -2,6 +2,7 @@ package token
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/crypto"
|
"github.com/authorizerdev/authorizer/server/crypto"
|
||||||
@@ -91,6 +92,7 @@ func ParseJWTToken(token, hostname, nonce, subject string) (jwt.MapClaims, error
|
|||||||
return claims, errors.New("invalid audience")
|
return claims, errors.New("invalid audience")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Println("claims:", claims["nonce"], nonce, claims["nonce"] == nonce)
|
||||||
if claims["nonce"] != nonce {
|
if claims["nonce"] != nonce {
|
||||||
return claims, errors.New("invalid nonce")
|
return claims, errors.New("invalid nonce")
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// CreateVerificationToken creates a verification JWT token
|
// CreateVerificationToken creates a verification JWT token
|
||||||
func CreateVerificationToken(email, tokenType, hostname, nonceHash string) (string, error) {
|
func CreateVerificationToken(email, tokenType, hostname, nonceHash, redirectURL string) (string, error) {
|
||||||
claims := jwt.MapClaims{
|
claims := jwt.MapClaims{
|
||||||
"iss": hostname,
|
"iss": hostname,
|
||||||
"aud": envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyClientID),
|
"aud": envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyClientID),
|
||||||
@@ -18,7 +18,7 @@ func CreateVerificationToken(email, tokenType, hostname, nonceHash string) (stri
|
|||||||
"iat": time.Now().Unix(),
|
"iat": time.Now().Unix(),
|
||||||
"token_type": tokenType,
|
"token_type": tokenType,
|
||||||
"nonce": nonceHash,
|
"nonce": nonceHash,
|
||||||
"redirect_url": envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAppURL),
|
"redirect_uri": redirectURL,
|
||||||
}
|
}
|
||||||
|
|
||||||
return SignJWTToken(claims)
|
return SignJWTToken(claims)
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<title>{{.data.organizationName}}</title>
|
<title>{{.data.organizationName}}</title>
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/app/favicon_io/apple-touch-icon.png">
|
<link rel="apple-touch-icon" sizes="180x180" href="/app/favicon_io/apple-touch-icon.png">
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/app/favicon_io/favicon-32x32.png">
|
<link rel="icon" type="image/png" sizes="32x32" href="/app/favicon_io/favicon-32x32.png">
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="/app/favicon_io/favicon-16x16.png">
|
<link rel="icon" type="image/png" sizes="16x16" href="/app/favicon_io/favicon-16x16.png">
|
||||||
|
@@ -8,7 +8,6 @@
|
|||||||
(function (window, document) {
|
(function (window, document) {
|
||||||
var targetOrigin = {{.target_origin}};
|
var targetOrigin = {{.target_origin}};
|
||||||
var authorizationResponse = {{.authorization_response}};
|
var authorizationResponse = {{.authorization_response}};
|
||||||
console.log({targetOrigin})
|
|
||||||
window.parent.postMessage(authorizationResponse, targetOrigin);
|
window.parent.postMessage(authorizationResponse, targetOrigin);
|
||||||
})(this, this.document);
|
})(this, this.document);
|
||||||
</script>
|
</script>
|
||||||
|
Reference in New Issue
Block a user