O Microsoft Graph é a porta de entrada para os serviços da Microsoft 365. Ele permite acessar informações como:
- Dados de usuários e grupos.
- Emails e calendários do Outlook.
- Arquivos no OneDrive.
- Mensagens no Teams.
Mas para chamar o Graph, não basta fazer um GET com http.Client.
É necessário autenticar via Microsoft Entra ID (antigo Azure Active Directory), obter um Access Token OAuth2, e então usá-lo em cada requisição.
👉 Isso pode complicar a aplicação principal, que precisa conhecer detalhes de autenticação.
Uma solução elegante é usar o padrão Sidecar: um serviço auxiliar, rodando ao lado da aplicação, que cuida dessa complexidade.
1. O que é um Sidecar?
Imagine que você tem uma aplicação (em Go, .NET, Python, etc) que precisa acessar o Graph.
Você pode embutir a lógica de autenticação dentro dela. Mas, se várias aplicações precisarem fazer isso, você terá código duplicado em todos os lugares.
O sidecar resolve esse problema:
- É um pequeno serviço separado (nesse caso em Go) que fica rodando ao lado da aplicação.
- Ele fala com o Microsoft Entra ID para buscar tokens.
- Ele chama o Microsoft Graph.
- Ele expõe uma API simples (via gRPC) para que a aplicação principal possa apenas pedir:
"Me dê os dados do usuário X"
E o sidecar cuida do resto.
📌 Em arquiteturas Kubernetes, o sidecar roda no mesmo pod da aplicação principal.
Isso significa que a aplicação acessa o sidecar por localhost, sem depender da rede externa.
2. Arquitetura
📌 O que acontece:
- A aplicação chama GetUserno sidecar.
- O sidecar verifica se já tem um Access Token válido em cache.
- Se não tiver, pede um novo ao Microsoft Entra ID.
- O sidecar chama o Microsoft Graph passando o token no header.
- O Graph devolve os dados do usuário.
- O sidecar converte para UserResponsee devolve para a aplicação via gRPC.
 
3. Definindo o contrato gRPC
Antes do código Go, precisamos definir o contrato de comunicação.
Crie o arquivo graph.proto:
syntax = "proto3";
package graph;
// Serviço que o sidecar vai expor
service GraphService {
  rpc GetUser (UserRequest) returns (UserResponse);
}
// Requisição: o cliente pode passar um user_id
// Se vazio, o sidecar consulta o "me" (usuário atual)
message UserRequest {
  string user_id = 1;
}
// Resposta: dados básicos do usuário no Graph
message UserResponse {
  string id = 1;
  string display_name = 2;
  string given_name = 3;
  string surname = 4;
  string user_principal_name = 5;
}
Explicando
- O serviço GraphServiceexpõe o métodoGetUser.
- 
UserRequestpermite consultar um usuário específico (users/{id}) ou o próprio usuário (me).
- 
UserResponseretorna alguns campos comuns do Graph.
Gerando código Go
Com o protoc instalado, rode:
protoc --go_out=. --go-grpc_out=. graph.proto
Isso gera os arquivos .pb.go, que contêm as interfaces gRPC que implementaremos.
4. Implementando o sidecar em Go
4.1 Estrutura base
Crie um arquivo sidecar.go:
package main
import (
    "bytes"
    "context"
    "encoding/json"
    "fmt"
    "io"
    "net"
    "net/http"
    "os"
    "sync"
    "time"
    pb "example.com/graph/proto" // ajuste para o caminho correto
    "google.golang.org/grpc"
)
const (
    tokenURL = "https://login.microsoftonline.com/%s/oauth2/v2.0/token"
    graphURL = "https://graph.microsoft.com/v1.0/%s"
)
// Servidor gRPC que vai rodar como sidecar
type server struct {
    pb.UnimplementedGraphServiceServer
    mu          sync.Mutex
    accessToken string
    expiration  time.Time
}
📌 Aqui definimos:
- As constantes com os endpoints de autenticação (Entra ID) e do Graph.
- 
Uma struct serverque:- Implementa nosso serviço gRPC.
- Guarda em memória (accessToken,expiration) o último token obtido.
 
4.2 Gerenciamento de Token
func (s *server) getToken(ctx context.Context) (string, error) {
    s.mu.Lock()
    defer s.mu.Unlock()
    // Se já temos token válido, reutiliza
    if time.Now().Before(s.expiration) && s.accessToken != "" {
        return s.accessToken, nil
    }
    // Senão, pede um novo token ao Entra ID
    tenantID := os.Getenv("ENTRA_TENANT_ID")
    clientID := os.Getenv("ENTRA_CLIENT_ID")
    clientSecret := os.Getenv("ENTRA_CLIENT_SECRET")
    url := fmt.Sprintf(tokenURL, tenantID)
    data := []byte(fmt.Sprintf(
        "client_id=%s&scope=https%%3A%%2F%%2Fgraph.microsoft.com%%2F.default&client_secret=%s&grant_type=client_credentials",
        clientID, clientSecret,
    ))
    req, _ := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(data))
    req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()
    body, _ := io.ReadAll(resp.Body)
    if resp.StatusCode != http.StatusOK {
        return "", fmt.Errorf("erro ao obter token: %s", string(body))
    }
    var token struct {
        AccessToken string `json:"access_token"`
        ExpiresIn   int    `json:"expires_in"`
    }
    if err := json.Unmarshal(body, &token); err != nil {
        return "", err
    }
    // Atualiza cache (renova 1 min antes do vencimento)
    s.accessToken = token.AccessToken
    s.expiration = time.Now().Add(time.Duration(token.ExpiresIn-60) * time.Second)
    return s.accessToken, nil
}
Explicando
- Cache de token: o sidecar não pede token toda hora → economiza requisições.
- 
Mutex (mu): garante que múltiplas chamadas concorrentes não façamPOSTao mesmo tempo.
- Renovação antecipada: 1 minuto antes de expirar, o sidecar já pede um novo.
  
  
  4.3 Implementando GetUser
func (s *server) GetUser(ctx context.Context, req *pb.UserRequest) (*pb.UserResponse, error) {
    // 1. Garantir token válido
    token, err := s.getToken(ctx)
    if err != nil {
        return nil, err
    }
    // 2. Montar endpoint
    userEndpoint := "me"
    if req.UserId != "" {
        userEndpoint = "users/" + req.UserId
    }
    url := fmt.Sprintf(graphURL, userEndpoint)
    httpReq, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
    httpReq.Header.Set("Authorization", "Bearer "+token)
    // 3. Fazer requisição ao Microsoft Graph
    resp, err := http.DefaultClient.Do(httpReq)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    body, _ := io.ReadAll(resp.Body)
    if resp.StatusCode != http.StatusOK {
        return nil, fmt.Errorf("erro ao buscar usuário: %s", string(body))
    }
    // 4. Mapear resposta para nosso UserResponse
    var user map[string]interface{}
    if err := json.Unmarshal(body, &user); err != nil {
        return nil, err
    }
    return &pb.UserResponse{
        Id:                user["id"].(string),
        DisplayName:       user["displayName"].(string),
        GivenName:         user["givenName"].(string),
        Surname:           user["surname"].(string),
        UserPrincipalName: user["userPrincipalName"].(string),
    }, nil
}
Explicando
- Pega o token em cache (ou renova).
- Define o endpoint (/meou/users/{id}).
- Faz um GETno Microsoft Graph.
- Converte a resposta JSON para a struct UserResponse.
4.4 Subindo o servidor gRPC
func main() {
    lis, err := net.Listen("tcp", ":50051")
    if err != nil {
        panic(err)
    }
    grpcServer := grpc.NewServer()
    pb.RegisterGraphServiceServer(grpcServer, &server{})
    fmt.Println("Sidecar Microsoft Graph rodando em :50051")
    if err := grpcServer.Serve(lis); err != nil {
        panic(err)
    }
}
Explicando
- O sidecar ouve na porta 50051.
- Ele registra o serviço GraphService.
- Ele fica rodando, pronto para responder às chamadas gRPC da aplicação principal.
5. Executando
- Configure variáveis de ambiente:
   export ENTRA_CLIENT_ID="seu-client-id"
   export ENTRA_CLIENT_SECRET="seu-client-secret"
   export ENTRA_TENANT_ID="seu-tenant-id"
- Rode o sidecar:
   go run sidecar.go
- Teste com grpcurl:
   grpcurl -plaintext -d '{}' localhost:50051 graph.GraphService/GetUser
6. Exemplo de resposta
{
  "id": "1234abcd-...",
  "display_name": "João Silva",
  "given_name": "João",
  "surname": "Silva",
  "user_principal_name": "joao@empresa.com"
}
7. Por que usar Sidecar?
- Simplicidade: a aplicação só chama gRPC, sem se preocupar com OAuth2.
- Reuso: múltiplos serviços podem compartilhar o mesmo sidecar.
- Segurança: credenciais ficam apenas no sidecar.
- Escalabilidade: sidecars podem ser replicados em pods diferentes.
- Observabilidade: métricas e logs podem ser centralizados no sidecar.
Conclusão
Criamos um sidecar em Go que:
- Autentica no Microsoft Entra ID via Client Credentials Flow.
- Usa o Microsoft Graph para consultar usuários.
- Exponde um serviço gRPC simples para a aplicação principal.
Com isso, conseguimos um design mais limpo, seguro e escalável, separando responsabilidades e aproveitando os benefícios do padrão Sidecar.
💡Curtiu?
Se quiser trocar ideia sobre IA, cloud e arquitetura, me segue nas redes:
Publico conteúdos técnicos direto do campo de batalha. E quando descubro uma ferramenta que economiza tempo e resolve bem, como essa, você fica sabendo também.
 
 
              

 
    
Top comments (0)