{"action":"created","issue":{"url":"https://api.github.com/repos/dgrijalva/jwt-go/issues/73","labels_url":"https://api.github.com/repos/dgrijalva/jwt-go/issues/73/labels{/name}","comments_url":"https://api.github.com/repos/dgrijalva/jwt-go/issues/73/comments","events_url":"https://api.github.com/repos/dgrijalva/jwt-go/issues/73/events","html_url":"https://github.com/dgrijalva/jwt-go/pull/73","id":95000380,"number":73,"title":"Custom parsing","user":{"login":"itsjamie","id":1956521,"avatar_url":"https://avatars.githubusercontent.com/u/1956521?v=3","gravatar_id":"","url":"https://api.github.com/users/itsjamie","html_url":"https://github.com/itsjamie","followers_url":"https://api.github.com/users/itsjamie/followers","following_url":"https://api.github.com/users/itsjamie/following{/other_user}","gists_url":"https://api.github.com/users/itsjamie/gists{/gist_id}","starred_url":"https://api.github.com/users/itsjamie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/itsjamie/subscriptions","organizations_url":"https://api.github.com/users/itsjamie/orgs","repos_url":"https://api.github.com/users/itsjamie/repos","events_url":"https://api.github.com/users/itsjamie/events{/privacy}","received_events_url":"https://api.github.com/users/itsjamie/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":1,"created_at":"2015-07-14T17:49:46Z","updated_at":"2015-07-14T18:09:08Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/dgrijalva/jwt-go/pulls/73","html_url":"https://github.com/dgrijalva/jwt-go/pull/73","diff_url":"https://github.com/dgrijalva/jwt-go/pull/73.diff","patch_url":"https://github.com/dgrijalva/jwt-go/pull/73.patch"},"body":"Another implementation where Claims can be an actual struct.\r\n\r\nUsed a lot of the same ideas as #66, however rather than making the interface have each check, I went with a interface of ```Valid() error```\r\n\r\nThis is still a breaking API change for where users access values from the Claims object currently, as they would need to cast to either jwt.MapClaim, or their object.\r\n\r\n_Example Implementation_ \r\n\r\nThe \"Can\" method on ScopeInfo is really why I want this. Without it, you have to reconstruct the object from a map[string]interface{}\r\n```go\r\ntype Claims struct {\r\n\tAudience  string `json:\"aud,omitempty\"`\r\n\tClient    string `json:\"cid,omitempty\"`\r\n\tExpiry    int64  `json:\"exp,omitempty\"`\r\n\tId        string `json:\"jti,omitempty\"`\r\n\tIssuedAt  int64  `json:\"iat,omitempty\"`\r\n\tIssuer    string `json:\"iss,omitempty\"`\r\n\tNotBefore int64  `json:\"nbf,omitempty\"`\r\n\tScope     Scope  `json:\"scope\"`\r\n\tSubject   string `json:\"sub,omitempty\"`\r\n}\r\n\r\nfunc (c Claims) Valid() error {\r\n\tnow := time.Now().Unix()\r\n\r\n\tif now > c.Expiry {\r\n\t\treturn errors.New(\"token is expired\")\r\n\t}\r\n\r\n\tif now < c.NotBefore {\r\n\t\treturn errors.New(\"token is not valid yet\")\r\n\t}\r\n\r\n\treturn nil\r\n}\r\n\r\ntype ScopeInfo struct {\r\n\tActions []string `json:\"actions\"`\r\n}\r\n\r\ntype Scope struct {\r\n\tUser ScopeInfo `json:\"user\"`\r\n}\r\n\r\nfunc (u *ScopeInfo) Can(scopes ...string) bool {\r\n\tprivledged := true\r\n\tfor _, scope := range scopes {\r\n\t\tmatched := false\r\n\t\tfor _, cmp := range u.Actions {\r\n\t\t\tif scope == cmp {\r\n\t\t\t\tmatched = true\r\n\t\t\t\tbreak\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif matched == false {\r\n\t\t\tprivledged = false\r\n\t\t\tbreak\r\n\t\t}\r\n\t}\r\n\treturn privledged\r\n}\r\n\r\n// Creating a token with specific claims\r\nclaims := Claims{}\r\nclaims.Audience = \"https://myapi.com\"\r\nclaims.Client = \"client-id\"\r\nclaims.Expiry = time.Now().Add(1 * time.Hour).Unix()\r\nclaims.IssuedAt = time.Now().Unix()\r\nclaims.Issuer = \"https://sso.myapi.com\"\r\nclaims.Scope = Scope{\r\n\tUser: ScopeInfo{\r\n\t\tActions: []string{\"read\"},\r\n\t},\r\n}\r\ntoken := jwt.NewWithClaims(jwt.GetSigningMethod(\"RS256\"), claims)\r\n\r\n\r\n//Parsing a token\r\ntoken, err := jwt.ParseWithClaims(activationToken, func(token *jwt.Token) (interface{}, error) {\r\n\tif _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {\r\n\t\treturn nil, fmt.Errorf(\"Unexpected signing method: %v\", token.Header[\"alg\"])\r\n\t}\r\n\treturn apiVerifyKey, nil\r\n}, &Claims{})\r\n\r\nif err != nil || token.Valid == false {\r\n\t//err handle\r\n}\r\n\r\nclaims := token.Claims.(*Claims)\r\nif !claims.Scope.User.Can(\"read\") {\r\n\t//I can use my structs!\r\n}\r\n```\r\n\r\n#64 "},"comment":{"url":"https://api.github.com/repos/dgrijalva/jwt-go/issues/comments/121326497","html_url":"https://github.com/dgrijalva/jwt-go/pull/73#issuecomment-121326497","issue_url":"https://api.github.com/repos/dgrijalva/jwt-go/issues/73","id":121326497,"user":{"login":"itsjamie","id":1956521,"avatar_url":"https://avatars.githubusercontent.com/u/1956521?v=3","gravatar_id":"","url":"https://api.github.com/users/itsjamie","html_url":"https://github.com/itsjamie","followers_url":"https://api.github.com/users/itsjamie/followers","following_url":"https://api.github.com/users/itsjamie/following{/other_user}","gists_url":"https://api.github.com/users/itsjamie/gists{/gist_id}","starred_url":"https://api.github.com/users/itsjamie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/itsjamie/subscriptions","organizations_url":"https://api.github.com/users/itsjamie/orgs","repos_url":"https://api.github.com/users/itsjamie/repos","events_url":"https://api.github.com/users/itsjamie/events{/privacy}","received_events_url":"https://api.github.com/users/itsjamie/received_events","type":"User","site_admin":false},"created_at":"2015-07-14T18:09:08Z","updated_at":"2015-07-14T18:09:08Z","body":"I chose to write a different approach because this allows the user the ability to write their own validation logic if they so choose. In this case, I would probably have the package make a few public functions like.. \r\n\r\nValidateIssuer\r\nValidateNBF\r\nValidate...\r\n\r\nWhich a user could use inside their own Valid() function.\r\n\r\nOne concern mentioned in #66 was if the interface changed it would require a breaking API change, however in this case, I don't foresee any reason why it would need to change (knockonwood)."}}