Implementing the callback routes
After the OAuth2 server has verified the authorization code, it will redirect the user to the callback URL. This callback URL is configured in the OAuth2 client settings. The callback endpoint exchanges the temporary authorization code for permanent access tokens while verifying security measures (PKCE challenge and state parameter) to prevent attacks.
- Expressjs
- Go
index.js
import session from "express-session"
app.use(
session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: true,
}),
)
app.get("/callback", async (req, res) => {
if (!config) {
throw new Error("Config not found")
}
try {
// Get the current URL
const currentUrl = new URL(req.url, `http://${req.headers.host}`)
// Exchange code for tokens with PKCE verification
const tokens = await client.authorizationCodeGrant(config, currentUrl, {
pkceCodeVerifier: req.session.codeVerifier,
expectedState: req.session.state,
})
// Store tokens in session
req.session.tokens = tokens
// Redirect to home page
res.redirect("/")
} catch (error) {
console.error("Callback error:", error)
res.status(500).send(`Authentication failed: ${error.message}`)
}
})
app.listen(3000, () => {
console.log("Server is running on port 3000")
})
main.go
import (
"context"
"encoding/json"
"fmt"
"net/http"
"golang.org/x/oauth2"
)
func handleCallback(w http.ResponseWriter, r *http.Request) {
// Get session cookie
cookie, err := r.Cookie("session_id")
if err != nil {
http.Error(w, "No session found", http.StatusBadRequest)
return
}
// Get session from store
session, ok := sessions[cookie.Value]
if !ok {
http.Error(w, "Invalid session", http.StatusBadRequest)
return
}
// Get authorization code and state from query parameters
code := r.URL.Query().Get("code")
state := r.URL.Query().Get("state")
if code == "" || state == "" || state != session.State {
http.Error(w, "Invalid request", http.StatusBadRequest)
return
}
// Exchange authorization code for token
token, err := oauthConfig.Exchange(
context.Background(),
code,
oauth2.VerifierOption(session.CodeVerifier),
)
if err != nil {
http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError)
return
}
// Update session with token
session.Token = token
sessions[cookie.Value] = session
// Fetch user info
client := oauthConfig.Client(context.Background(), token)
userInfoURL := fmt.Sprintf("https://%s.projects.oryapis.com/userinfo", projectSlug)
resp, err := client.Get(userInfoURL)
if err != nil {
http.Error(w, "Failed to fetch user info: "+err.Error(), http.StatusInternalServerError)
return
}
defer resp.Body.Close()
// Parse user info response
var userInfo map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&userInfo); err != nil {
http.Error(w, "Failed to parse user info: "+err.Error(), http.StatusInternalServerError)
return
}
// Update session with user info
session.UserInfo = userInfo
sessions[cookie.Value] = session
// Redirect to home page
http.Redirect(w, r, "/", http.StatusSeeOther)
}