github.com/thetreep/go-swagger@v0.0.0-20240223100711-35af64f14f01/examples/composed-auth/auth/authorizers.go (about) 1 package auth 2 3 import ( 4 "crypto/rsa" 5 "os" 6 7 errors "github.com/go-openapi/errors" 8 jwt "github.com/golang-jwt/jwt/v5" 9 models "github.com/thetreep/go-swagger/examples/composed-auth/models" 10 ) 11 12 const ( 13 // currently unused: privateKeyPath = "keys/apiKey.prv" 14 publicKeyPath = "keys/apiKey.pem" 15 issuerName = "example.com" 16 ) 17 18 var ( 19 userDb map[string]string 20 21 // Keys used to sign and verify our tokens 22 verifyKey *rsa.PublicKey 23 // currently unused: signKey *rsa.PrivateKey 24 ) 25 26 // roleClaims describes the format of our JWT token's claims 27 type roleClaims struct { 28 Roles []string `json:"roles"` 29 jwt.MapClaims 30 } 31 32 func init() { 33 // emulates the loading of a local users database 34 userDb = map[string]string{ 35 "fred": "scrum", 36 "ivan": "terrible", 37 } 38 39 // loads public keys to verify our tokens 40 verifyKeyBuf, err := os.ReadFile(publicKeyPath) 41 if err != nil { 42 panic("Cannot load public key for tokens") 43 } 44 verifyKey, err = jwt.ParseRSAPublicKeyFromPEM(verifyKeyBuf) 45 if err != nil { 46 panic("Invalid public key for tokens") 47 } 48 } 49 50 // Customized authorizer methods for our sample API 51 52 // IsRegistered determines if the user is properly registered, 53 // i.e if a valid username:password pair has been provided 54 func IsRegistered(user, pass string) (*models.Principal, error) { 55 password, ok := userDb[user] 56 if !ok || pass != password { 57 return nil, errors.New(401, "Unauthorized: not a registered user") 58 } 59 60 return &models.Principal{ 61 Name: user, 62 }, nil 63 } 64 65 // IsReseller tells if the API key is a JWT signed by us with a claim to be a reseller 66 func IsReseller(token string) (*models.Principal, error) { 67 claims, err := parseAndCheckToken(token) 68 if err != nil { 69 return nil, errors.New(401, "Unauthorized: invalid API key token: %v", err) 70 } 71 72 issuer, err := claims.GetIssuer() 73 if issuer != issuerName { 74 return nil, errors.New(401, "Unauthorized: invalid Bearer token: invalid issuer %v", err) 75 } 76 77 id, err := claims.GetSubject() 78 if err != nil { 79 return nil, errors.New(401, "Unauthorized: invalid Bearer token: missing subject: %v", err) 80 } 81 82 if issuer != issuerName || id == "" { 83 return nil, errors.New(403, "Forbidden: insufficient API key privileges") 84 } 85 86 isReseller := false 87 for _, role := range claims.Roles { 88 if role == "reseller" { 89 isReseller = true 90 break 91 } 92 } 93 94 if !isReseller { 95 return nil, errors.New(403, "Forbidden: insufficient API key privileges") 96 } 97 98 return &models.Principal{ 99 Name: id, 100 }, nil 101 } 102 103 // HasRole tells if the Bearer token is a JWT signed by us with a claim to be 104 // member of an authorization scope. 105 // We verify that the claimed role is one of the passed scopes 106 func HasRole(token string, scopes []string) (*models.Principal, error) { 107 claims, err := parseAndCheckToken(token) 108 if err != nil { 109 return nil, errors.New(401, "Unauthorized: invalid Bearer token: %v", err) 110 } 111 issuer, err := claims.GetIssuer() 112 if err != nil { 113 return nil, errors.New(401, "Unauthorized: invalid Bearer token: invalid issuer %v", err) 114 } 115 116 if issuer != issuerName { 117 return nil, errors.New(401, "Unauthorized: invalid Bearer token: invalid issuer %s", issuer) 118 } 119 120 id, err := claims.GetSubject() 121 if err != nil { 122 return nil, errors.New(401, "Unauthorized: invalid Bearer token: missing subject: %v", err) 123 } 124 125 isInScopes := false 126 claimedRoles := []string{} 127 for _, scope := range scopes { 128 for _, role := range claims.Roles { 129 if role == scope { 130 isInScopes = true 131 // we enrich the principal with all claimed roles within scope (hence: not breaking here) 132 claimedRoles = append(claimedRoles, role) 133 } 134 } 135 } 136 137 if !isInScopes { 138 return nil, errors.New(403, "Forbidden: insufficient privileges") 139 } 140 141 return &models.Principal{ 142 Name: id, 143 Roles: claimedRoles, 144 }, nil 145 } 146 147 func parseAndCheckToken(token string) (*roleClaims, error) { 148 // the API key is a JWT signed by us with a claim to be a reseller 149 parsedToken, err := jwt.ParseWithClaims( 150 token, &roleClaims{}, func(parsedToken *jwt.Token) (interface{}, error) { 151 // the key used to validate tokens 152 return verifyKey, nil 153 }, 154 ) 155 156 if err != nil { 157 return nil, err 158 } 159 160 claims, ok := parsedToken.Claims.(*roleClaims) 161 if !ok || !parsedToken.Valid { 162 return nil, errors.New(401, "invalid token missing expected claims") 163 } 164 165 return claims, nil 166 }