verifyMagicToken runs the HMAC + exp + action whitelist checks and returns either claims or an (HTTP status, user-visible message) pair. Shared by GET and POST so both paths reject the same inputs the same way. HMAC verification uses the deployment signer (a.approvalSigner, keyed on cfg.Signing.HMA
(token, endpointAction string)
| 137 | // HMAC verification uses the deployment signer (a.approvalSigner, keyed |
| 138 | // on cfg.Signing.HMACSecret) — the sole signer for magic-link tokens. |
| 139 | func (a *API) verifyMagicToken(token, endpointAction string) (*approvaltoken.Claims, int, string) { |
| 140 | if token == "" { |
| 141 | return nil, http.StatusBadRequest, |
| 142 | "This approval link is missing its token." |
| 143 | } |
| 144 | |
| 145 | var ( |
| 146 | claims *approvaltoken.Claims |
| 147 | err error |
| 148 | ) |
| 149 | if a.approvalSigner != nil { |
| 150 | claims, err = a.approvalSigner.Verify(token) |
| 151 | } else { |
| 152 | err = approvaltoken.ErrInvalidToken |
| 153 | } |
| 154 | if err != nil { |
| 155 | if errors.Is(err, approvaltoken.ErrTokenExpired) { |
| 156 | return nil, http.StatusGone, |
| 157 | "This approval link has expired. Visit the dashboard to review pending messages." |
| 158 | } |
| 159 | return nil, http.StatusBadRequest, "This approval link isn't valid." |
| 160 | } |
| 161 | if claims.Action != endpointAction { |
| 162 | return nil, http.StatusBadRequest, |
| 163 | "This approval link isn't valid for this action." |
| 164 | } |
| 165 | return claims, 0, "" |
| 166 | } |
| 167 | |
| 168 | // --- Action implementations (called after POST + token verify) --- |
| 169 |
no test coverage detected