Compare commits

...
Author SHA1 Message Date
adminandClaude Opus 4.5 0a612d614e Fix auth service deployment not updating container
The crane+docker-load approach was causing image tagging issues where the
loaded image didn't have the expected registry tag, causing docker-compose
to not find the correct image.

Changes:
- Use simple `docker pull` instead of crane pull + docker load (since
  deploy server is already logged into the registry)
- Add verification that the running container is using the expected image
- Fail the workflow if container is running the wrong image

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 07:40:04 -08:00
adminandClaude Opus 4.5 bdd985cd06 Add Apple OAuth debugging and GitHub email fetch
- Add comprehensive logging to Apple callback handler
- Fetch GitHub email from /user/emails when not in main response

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 07:28:20 -08:00
2 changed files with 31 additions and 14 deletions
+17 -14
View File
@@ -152,28 +152,31 @@ jobs:
echo "Deploying auth service: $AUTH_IMAGE"
# Use crane to pull image
echo "Installing crane..."
curl -sL https://github.com/google/go-containerregistry/releases/download/v0.20.2/go-containerregistry_Linux_x86_64.tar.gz | tar xzf - crane
chmod +x crane
echo "Pulling Auth image with crane..."
./crane pull "${AUTH_IMAGE}" auth.tar || { echo "ERROR: Failed to pull auth image"; exit 1; }
echo "Loading Auth image into Docker..."
docker load -i auth.tar
rm auth.tar
rm ./crane
# Pull the image directly (docker is already logged in)
echo "Pulling Auth image..."
docker pull "${AUTH_IMAGE}" || { echo "ERROR: Failed to pull auth image"; exit 1; }
# Only recreate the auth container (not eagle, shardok, etc.)
docker compose -f docker-compose.prod.yml up -d --no-deps --force-recreate auth
# Wait for health check
sleep 5
docker compose -f docker-compose.prod.yml ps auth
# Verify container is using correct image
# Verify container is using the correct image
echo "=== Verifying auth container image ==="
docker compose -f docker-compose.prod.yml images auth
RUNNING_IMAGE=$(docker inspect auth-server --format '{{.Config.Image}}')
echo "Expected image: ${AUTH_IMAGE}"
echo "Running image: ${RUNNING_IMAGE}"
if [ "$RUNNING_IMAGE" != "$AUTH_IMAGE" ]; then
echo "ERROR: Container is running wrong image!"
echo "Container details:"
docker inspect auth-server --format '{{.Id}} {{.Config.Image}}'
exit 1
fi
# Show container status
docker compose -f docker-compose.prod.yml ps auth
# Cleanup old images
docker image prune -f
@@ -8,6 +8,7 @@ import (
"encoding/pem"
"fmt"
"io"
"log"
"net/http"
"net/url"
"os"
@@ -111,6 +112,8 @@ func (s *OAuthService) ExchangeAppleCode(config OAuthProviderConfig, code string
data.Set("code", code)
data.Set("redirect_uri", callbackURL)
log.Printf("Apple token exchange: client_id=%s redirect_uri=%s", config.ClientID, callbackURL)
req, err := http.NewRequest("POST", config.TokenEndpoint, strings.NewReader(data.Encode()))
if err != nil {
return nil, err
@@ -180,6 +183,8 @@ func ParseAppleIDToken(idToken string) (*ProviderUserInfo, error) {
// HandleAppleCallback handles the Apple OAuth callback
// Note: Apple sends a POST request to the callback URL, not GET
func (s *OAuthService) HandleAppleCallback(w http.ResponseWriter, r *http.Request) {
log.Printf("Apple callback received: method=%s", r.Method)
// Apple POSTs the callback data
if r.Method != "POST" {
// Fallback to GET for the state parameter only
@@ -188,17 +193,20 @@ func (s *OAuthService) HandleAppleCallback(w http.ResponseWriter, r *http.Reques
// This might be an error redirect
errorParam := r.URL.Query().Get("error")
if errorParam != "" {
log.Printf("Apple callback error (GET): %s", errorParam)
s.completeWithError(state, fmt.Sprintf("Apple OAuth error: %s", errorParam))
http.Error(w, "OAuth failed: "+errorParam, http.StatusBadRequest)
return
}
}
log.Printf("Apple callback rejected: not POST")
http.Error(w, "Apple Sign-In requires POST callback", http.StatusMethodNotAllowed)
return
}
// Parse form data
if err := r.ParseForm(); err != nil {
log.Printf("Apple callback: failed to parse form: %v", err)
http.Error(w, "Failed to parse form data", http.StatusBadRequest)
return
}
@@ -207,13 +215,17 @@ func (s *OAuthService) HandleAppleCallback(w http.ResponseWriter, r *http.Reques
state := r.FormValue("state")
errorParam := r.FormValue("error")
log.Printf("Apple callback: state=%s hasCode=%v error=%s", state, code != "", errorParam)
if errorParam != "" {
log.Printf("Apple callback error (POST): %s", errorParam)
s.completeWithError(state, fmt.Sprintf("Apple OAuth error: %s", errorParam))
http.Error(w, "OAuth failed: "+errorParam, http.StatusBadRequest)
return
}
if code == "" || state == "" {
log.Printf("Apple callback: missing code or state")
http.Error(w, "Missing code or state", http.StatusBadRequest)
return
}
@@ -221,6 +233,7 @@ func (s *OAuthService) HandleAppleCallback(w http.ResponseWriter, r *http.Reques
// Get pending OAuth info
stateData, ok := s.pendingOAuth.Load(state)
if !ok {
log.Printf("Apple callback: state not found: %s", state)
http.Error(w, "Invalid or expired state", http.StatusBadRequest)
return
}
@@ -237,6 +250,7 @@ func (s *OAuthService) HandleAppleCallback(w http.ResponseWriter, r *http.Reques
config := s.configs[oauthState.Provider]
tokenResp, err := s.ExchangeAppleCode(config, code)
if err != nil {
log.Printf("Apple token exchange failed: %v", err)
s.completeWithError(state, err.Error())
http.Error(w, "Token exchange failed", http.StatusInternalServerError)
return