|
| 1 | +--- |
| 2 | +page_id: 8d8234a5-b064-47a1-a1e1-9b3f98a57f34 |
| 3 | +title: Call your API using device authorization flow |
| 4 | +sidebar: |
| 5 | + order: 3 |
| 6 | +relatedArticles: |
| 7 | + - 2944d2bc-4e84-4918-b4f6-7406a7c26f98 |
| 8 | + - 888b1546-8047-4609-af59-8cf859527aa0 |
| 9 | + - de937e16-8094-4aad-ada9-e6a37d74f508 |
| 10 | + - 1cbd91d2-c0b3-45b3-b038-319de1b2c794 |
| 11 | +--- |
| 12 | + |
| 13 | +Once you've received an access token from the device authorization flow, you can use it to call your protected APIs. This guide shows you how to validate tokens, handle scopes, and make authenticated API requests. |
| 14 | + |
| 15 | +## Use the access token from the device authorization flow |
| 16 | + |
| 17 | +The access token you receive from the device authorization flow is a standard OAuth 2.0 Bearer token. Include it in the `Authorization` header of your API requests: |
| 18 | + |
| 19 | +```bash |
| 20 | +curl -X GET https://your-api.com/protected-resource \ |
| 21 | + -H "Authorization: Bearer YOUR_ACCESS_TOKEN" |
| 22 | +``` |
| 23 | + |
| 24 | +## Token validation in the device authorization flow |
| 25 | + |
| 26 | +Before processing API requests, validate the access token to ensure it's valid and hasn't expired: |
| 27 | + |
| 28 | +### Validate with Kinde's userinfo endpoint |
| 29 | + |
| 30 | +```bash |
| 31 | +curl -X GET https://<your-subdomain>.kinde.com/oauth2/v2/user_profile \ |
| 32 | + -H "Authorization: Bearer YOUR_ACCESS_TOKEN" |
| 33 | +``` |
| 34 | + |
| 35 | +**Success response**: |
| 36 | + |
| 37 | +```json |
| 38 | +{ |
| 39 | + "sub": "kp_c3143a4b50ad43c88e541d9077681782", |
| 40 | + "provided_id": "some_external_id", |
| 41 | + "name": "John Snow", |
| 42 | + "given_name": "John", |
| 43 | + "family_name": "Snow", |
| 44 | + "updated_at": 1612345678, |
| 45 | + |
| 46 | + "email_verified": true, |
| 47 | + "picture": "https://example.com/john_snow.jpg", |
| 48 | + "preferred_username": "john_snow", |
| 49 | + "id": "kp_c3143a4b50ad43c88e541d9077681782" |
| 50 | +} |
| 51 | +``` |
| 52 | + |
| 53 | +**Error response** (invalid token): |
| 54 | + |
| 55 | +```json |
| 56 | +{ |
| 57 | + "error": "invalid_token", |
| 58 | + "error_description": "The access token is invalid or expired" |
| 59 | +} |
| 60 | +``` |
| 61 | + |
| 62 | +### Validate with your own API |
| 63 | + |
| 64 | +You can also validate tokens in your own API by verifying the JWT signature and claims: |
| 65 | + |
| 66 | +```javascript |
| 67 | ++// Node.js example using jsonwebtoken with JWKS |
| 68 | ++const jwt = require("jsonwebtoken"); |
| 69 | ++const jwksClient = require("jwks-rsa"); |
| 70 | ++ |
| 71 | ++const client = jwksClient({ |
| 72 | ++ jwksUri: "https://<your-subdomain>.kinde.com/.well-known/jwks" |
| 73 | ++}); |
| 74 | ++ |
| 75 | ++function getKey(header, callback) { |
| 76 | ++ client.getSigningKey(header.kid, (err, key) => { |
| 77 | ++ const signingKey = key.publicKey || key.rsaPublicKey; |
| 78 | ++ callback(null, signingKey); |
| 79 | ++ }); |
| 80 | ++} |
| 81 | ++ |
| 82 | ++function validateToken(token) { |
| 83 | ++ return new Promise((resolve, reject) => { |
| 84 | ++ jwt.verify(token, getKey, { algorithms: ["RS256"] }, (err, decoded) => { |
| 85 | ++ if (err) { |
| 86 | ++ resolve({ valid: false, error: err.message }); |
| 87 | ++ } else { |
| 88 | ++ resolve({ valid: true, user: decoded }); |
| 89 | ++ } |
| 90 | ++ }); |
| 91 | ++ }); |
| 92 | ++} |
| 93 | +``` |
| 94 | + |
| 95 | +## Scope enforcement for device authorization |
| 96 | + |
| 97 | +Access tokens include scopes that determine what resources the user can access. Check the required scopes before processing requests: |
| 98 | + |
| 99 | +```javascript |
| 100 | +// Example: Check if user has required scope |
| 101 | +function hasRequiredScope(token, requiredScope) { |
| 102 | + const decoded = jwt.decode(token); |
| 103 | + const tokenScopes = decoded.scope.split(" "); |
| 104 | + return tokenScopes.includes(requiredScope); |
| 105 | +} |
| 106 | + |
| 107 | +// Usage |
| 108 | +if (!hasRequiredScope(accessToken, "read:users")) { |
| 109 | + return res.status(403).json({error: "Insufficient scope"}); |
| 110 | +} |
| 111 | +``` |
| 112 | + |
| 113 | +## Common API patterns for device authorization |
| 114 | + |
| 115 | +### Protected resource endpoint |
| 116 | + |
| 117 | +```javascript |
| 118 | +// Express.js example |
| 119 | +app.get("/api/protected-resource", authenticateToken, (req, res) => { |
| 120 | + // req.user contains the decoded token payload |
| 121 | + res.json({ |
| 122 | + message: "Access granted", |
| 123 | + user: req.user |
| 124 | + }); |
| 125 | +}); |
| 126 | + |
| 127 | +function authenticateToken(req, res, next) { |
| 128 | + const authHeader = req.headers["authorization"]; |
| 129 | + const token = authHeader && authHeader.split(" ")[1]; |
| 130 | + |
| 131 | + if (!token) { |
| 132 | + return res.status(401).json({error: "Access token required"}); |
| 133 | + } |
| 134 | + |
| 135 | + // Validate token with Kinde |
| 136 | + fetch("https://<your-subdomain>.kinde.com/oauth2/v2/user_profile", { |
| 137 | + headers: { |
| 138 | + Authorization: `Bearer ${token}` |
| 139 | + } |
| 140 | + }) |
| 141 | + .then((response) => { |
| 142 | + if (!response.ok) { |
| 143 | + throw new Error("Invalid token"); |
| 144 | + } |
| 145 | + return response.json(); |
| 146 | + }) |
| 147 | + .then((user) => { |
| 148 | + req.user = user; |
| 149 | + next(); |
| 150 | + }) |
| 151 | + .catch((error) => { |
| 152 | + return res.status(401).json({error: "Invalid token"}); |
| 153 | + }); |
| 154 | +} |
| 155 | +``` |
| 156 | + |
| 157 | +### Error handling for device authorization |
| 158 | + |
| 159 | +Handle common token-related errors: |
| 160 | + |
| 161 | +```javascript |
| 162 | +function handleTokenError(res, error) { |
| 163 | + switch (error.error) { |
| 164 | + case "invalid_token": |
| 165 | + // Token is invalid or expired |
| 166 | + return res.status(401).json({error: "Please re-authenticate"}); |
| 167 | + |
| 168 | + case "insufficient_scope": |
| 169 | + // Token doesn't have required permissions |
| 170 | + return res.status(403).json({error: "Insufficient permissions"}); |
| 171 | + |
| 172 | + default: |
| 173 | + return res.status(500).json({error: "Authentication error"}); |
| 174 | + } |
| 175 | +} |
| 176 | +``` |
| 177 | + |
| 178 | +## Security best practices for device authorization |
| 179 | + |
| 180 | +### Token storage |
| 181 | + |
| 182 | +- **Never store tokens in localStorage**: Use secure HTTP-only cookies or memory storage |
| 183 | +- **Validate tokens server-side**: Always validate tokens on your backend, not just the client |
| 184 | + |
| 185 | +### Rate limiting |
| 186 | + |
| 187 | +Implement rate limiting for token validation requests: |
| 188 | + |
| 189 | +```javascript |
| 190 | +const rateLimit = require("express-rate-limit"); |
| 191 | + |
| 192 | +const tokenValidationLimiter = rateLimit({ |
| 193 | + windowMs: 15 * 60 * 1000, // 15 minutes |
| 194 | + max: 100, // limit each IP to 100 requests per windowMs |
| 195 | + message: "Too many token validation requests" |
| 196 | +}); |
| 197 | + |
| 198 | +app.use("/api/protected-resource", tokenValidationLimiter); |
| 199 | +``` |
| 200 | + |
| 201 | +### Logging and monitoring |
| 202 | + |
| 203 | +Log authentication events for security monitoring: |
| 204 | + |
| 205 | +```javascript |
| 206 | +function logAuthEvent(token, action, success) { |
| 207 | + console.log({ |
| 208 | + timestamp: new Date().toISOString(), |
| 209 | + action: action, |
| 210 | + success: success, |
| 211 | + userId: token.user_id, |
| 212 | + scopes: token.scope |
| 213 | + }); |
| 214 | +} |
| 215 | +``` |
| 216 | + |
| 217 | +## Testing your API |
| 218 | + |
| 219 | +Test your protected endpoints with the access token: |
| 220 | + |
| 221 | +```bash |
| 222 | +# Test with curl |
| 223 | +curl -X GET https://your-api.com/protected-resource \ |
| 224 | + -H "Authorization: Bearer YOUR_ACCESS_TOKEN" |
| 225 | + |
| 226 | +# Test with JavaScript |
| 227 | +fetch('https://your-api.com/protected-resource', { |
| 228 | + headers: { |
| 229 | + 'Authorization': 'Bearer YOUR_ACCESS_TOKEN' |
| 230 | + } |
| 231 | +}) |
| 232 | +.then(response => response.json()) |
| 233 | +.then(data => console.log(data)); |
| 234 | +``` |
0 commit comments