En sikkerhetsproxy som begrenser tilgang til Microsoft Graph API ressurser basert på Azure AD-grupper.
Problem: Microsoft Graph API gir tilgang til ALLE ressurser i organisasjonen - ikke ideelt når team/avdelinger kun skal se sine egne rom og utstyr.
Løsning: GraphScope Proxy fungerer som et tilgangskontroll-lag som:
- 🔐 Gruppe-basert tilgang: Brukere logger inn med API-nøkkel + Azure AD gruppe-ID
- 🌐 Transparent proxying: Speiler alle Graph API endepunkter (
/v1.0/*,/beta/*) 1:1 - 🛡️ Automatisk filtrering: Returnerer kun ressurser (rom/utstyr) brukeren har tilgang til
- 🎫 JWT sikkerhet: Realtime validering av alle API-kall
Resultat:
Før: Klient ser ALLE 500 rom i organisasjonen
Etter: Klient ser kun de 12 rommene i sin avdeling
Bruksområder: Facility management, meeting booking, resource planning, multi-tenant Graph API deling.
Status: ✅ MVP FERDIG - Alle kjernekomponenter er implementert og funksjonelle!
Teknisk flyt: Client logs in with
apiKey+groupId→ proxy builds a permissions list of rooms in the group → issues a JWT with embedded resource scope → all subsequent Graph calls are filtered/rejected in real-time based on this scope.
See detailed concept and solution plan in: modelplanning.md and dotnet-architecture.md
- 1:1 proxying of Microsoft Graph (transparent to client)
- Scope limitation per Azure AD / Entra ID group
- Minimal JWT (only tokenId, groupId, count)
- Server-side cache of resource list (Memory / Redis)
- Response filtering for room/places endpoints
- Structured logging with correlation IDs
- Clear extension path for rate limiting and metrics
| Area | Choice | Version |
|---|---|---|
| Runtime | .NET | 9.0 |
| Web Framework | ASP.NET Core | 9.0 |
| HTTP Client | HttpClient + Microsoft Graph SDK | 5.x |
| Authentication | JWT (HS256) + API Keys | Built-in |
| Logging | ILogger + Serilog (JSON) | Built-in |
| Cache | IMemoryCache / Redis | Built-in |
| Container | Docker + Compose | Latest |
| Testing | xUnit + Testcontainers | Latest |
✅ Fullstendig implementert flyt:
- Login: Klient kaller
POST /auth/loginmedapiKey+groupId - Validering: Proxy validerer API key mot konfigurerte nøkler og gruppe-tilgang
- Graph Integration: Henter app access token og gruppe-medlemmer fra Microsoft Graph
- Resource Classification: Klassifiserer medlemmer som Room/Workspace/Equipment basert på navn/email-mønstre
- JWT Generation: Genererer JWT med resource scope embedded i claims
- Caching: Lagrer resource scope i cache med configurable TTL
- Proxy Calls: Alle videre kall til
/v1.0/*og/beta/*endpoints proxies med scope enforcement - Response Filtering: Filtrerer responser basert på brukerens tilgjengelige ressurser
GraphScopeProxy/
├── src/
│ ├── GraphScopeProxy.Api/ # REST API og controllers
│ │ ├── Controllers/
│ │ │ ├── AuthController.cs # ✅ Login/logout/refresh
│ │ │ ├── ProxyController.cs # ✅ v1.0 Graph API proxy
│ │ │ ├── BetaProxyController.cs # ✅ beta Graph API proxy
│ │ │ └── AdminController.cs # ✅ Health checks og admin
│ │ ├── Middleware/
│ │ │ ├── ResourceScopeMiddleware.cs # ✅ JWT validering og scope enforcement
│ │ │ └── ErrorHandlingMiddleware.cs # ✅ Global error handling
│ │ ├── GraphHealthCheck.cs # ✅ Health check implementation
│ │ ├── Program.cs # ✅ Application startup
│ │ ├── appsettings.json # Configuration (template)
│ │ └── GraphScopeProxy.Api.csproj
│ └── GraphScopeProxy.Core/ # Core business logic
│ ├── Services/
│ │ ├── GraphApiService.cs # ✅ Microsoft Graph SDK integration
│ │ ├── GraphProxyService.cs # ✅ HTTP proxy implementation
│ │ ├── GraphTokenService.cs # ✅ Graph API token management
│ │ ├── ResourceClassifier.cs # ✅ Resource type klassifisering
│ │ ├── JwtService.cs # ✅ JWT generering og validering
│ │ ├── ApiKeyService.cs # ✅ API key til gruppe mapping
│ │ ├── MemoryScopeCache.cs # ✅ In-memory cache implementation
│ │ ├── RedisScopeCache.cs # ✅ Redis cache implementation
│ │ └── I*.cs # ✅ Service interfaces
│ ├── Models/
│ │ ├── AllowedResource.cs # ✅ Resource data model
│ │ ├── ResourceScope.cs # ✅ User scope model
│ │ ├── ResourceType.cs # ✅ Resource type enum
│ │ ├── LoginRequest.cs # ✅ Auth request model
│ │ └── LoginResponse.cs # ✅ Auth response model
│ ├── Configuration/
│ │ └── GraphScopeOptions.cs # ✅ Configuration options
│ └── GraphScopeProxy.Core.csproj
├── tests/
│ ├── GraphScopeProxy.Tests/ # ✅ 22 unit tests passerer
│ │ ├── Unit/
│ │ │ └── Services/ # Service unit tests
│ │ └── GraphScopeProxy.Tests.csproj
│ └── GraphScopeProxy.IntegrationTests/ # Integration test struktur
│ └── GraphScopeProxy.IntegrationTests.csproj
├── docker-compose.yml # ✅ Docker Compose configuration
├── Dockerfile # ✅ Container image definition
├── GraphScopeProxy.sln # ✅ Solution file
├── README.md # ✅ Project documentation
├── MVP-STATUS.md # ✅ Implementation status
├── modelplanning.md # ✅ Architecture planning
├── dotnet-architecture.md # ✅ Technical architecture
├── IMPLEMENTATION-STATUS.md # ✅ Detailed implementation tracking
└── LICENSE # ✅ Project license
| Variabel | Beskrivelse | Eksempel | Default |
|---|---|---|---|
MS_TENANT_ID |
Entra ID tenant | xxxxxxxx-... |
– |
MS_CLIENT_ID |
App (client) ID | yyyyyyyy-... |
– |
MS_CLIENT_SECRET |
App secret | (hemmelig) | – |
GS_ALLOWED_PLACE_TYPES |
Komma-separert typer | room,workspace,equipment |
room,workspace |
GS_ALLOW_GENERIC_RESOURCES |
Ta med generics | true/false |
false |
GS_SCOPE_CACHE_TTL |
TTL sekunder | 900 |
900 |
GS_MAX_SCOPE_SIZE |
Øvre grense ressurser | 500 |
500 |
GS_REQUIRE_GROUP_ALIAS |
Krev alias | true/false |
false |
API_KEYS |
Komma-separert API-nøkler | key1,key2 |
– |
JWT_SIGNING_KEY / JWT_PRIVATE_KEY |
Nøkkel for signering | (streng / PEM) | – |
(Endelig navnkonvensjon kan justeres under implementasjon.)
- .NET 9.0 SDK
- Docker (optional)
- Azure AD App Registration with Microsoft Graph permissions:
Group.Read.All(to read group members)Places.Read.All(optional, for Places API)
git clone <repo-url> && cd GraphScopeProxy
# Set up configuration (create appsettings.Development.json)
dotnet user-secrets init --project src/GraphScopeProxy.Api
dotnet user-secrets set "GraphScope:TenantId" "your-tenant-id" --project src/GraphScopeProxy.Api
dotnet user-secrets set "GraphScope:ClientId" "your-client-id" --project src/GraphScopeProxy.Api
dotnet user-secrets set "GraphScope:ClientSecret" "your-client-secret" --project src/GraphScopeProxy.Api# Restore packages
dotnet restore
# Run tests to verify everything works
dotnet test
# Run the API
dotnet run --project src/GraphScopeProxy.Api
# Or with watch for development
dotnet watch --project src/GraphScopeProxy.Api
# API will be available at http://localhost:5000 (HTTP) and https://localhost:5001 (HTTPS)
### 3. Docker (Recommended)
```bash
# Build and start with Docker Compose
docker-compose up --build
# Health check
curl -s http://localhost:8080/health
The application uses appsettings.json for configuration. Key settings:
{
"GraphScope": {
"TenantId": "your-azure-tenant-id",
"ClientId": "your-app-client-id",
"ClientSecret": "your-app-secret",
"JwtIssuer": "GraphScopeProxy",
"JwtAudience": "GraphScopeProxy-Users",
"JwtSigningKey": "your-256-bit-signing-key",
"JwtExpirationSeconds": 900,
"AllowedPlaceTypes": ["room", "workspace", "equipment"],
"AllowGenericResources": false,
"MaxScopeSize": 500,
"UseGraphPlacesApi": true
},
"ApiKeys": {
"demo-key-1": ["group-id-1", "group-id-2"],
"your-api-key": ["your-group-id"]
}
}# Check health
curl http://localhost:5000/health
# View API documentation
# Open http://localhost:5000 in browser (Swagger UI)
# Test login (demo mode)
curl -X POST http://localhost:5000/auth/login \
-H "Content-Type: application/json" \
-d '{"apiKey": "demo-key-1", "groupId": "demo-group-1"}'| Metode | Path | Beskrivelse | Status |
|---|---|---|---|
POST |
/auth/login |
Autentiser og få JWT token | ✅ |
POST |
/auth/refresh |
Forny JWT token | ✅ |
POST |
/auth/logout |
Logg ut og invalider token | ✅ |
GET |
/health |
Health check endpoint | ✅ |
ANY |
/v1.0/* |
Proxy til Microsoft Graph v1.0 | ✅ |
ANY |
/beta/* |
Proxy til Microsoft Graph beta | ✅ |
Request:
curl -X POST http://localhost:5000/auth/login \
-H "Content-Type: application/json" \
-d '{
"apiKey": "your-api-key",
"groupId": "your-group-id"
}'Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"groupId": "your-group-id",
"resourceCount": 37,
"expiresIn": 900
}Authorization header for API calls:
Authorization: Bearer {JWT-token}
# Hent kalender for en ressurs (filtreres automatisk)
curl -H "Authorization: Bearer {JWT}" \
http://localhost:5000/v1.0/users/room1@company.com/calendar/events
# Hent rooms (kun de brukeren har tilgang til)
curl -H "Authorization: Bearer {JWT}" \
http://localhost:5000/v1.0/places/microsoft.graph.room✅ Implementerte sikkerhetstiltak:
- API Key Authentication: Statisk API nøkler med gruppe-mapping
- JWT Tokens: HS256 signering med konfigurerbar expiration (default 15 min)
- Resource Scope Enforcement: Kun tilgang til autoriserte ressurser
- Input Validation: Validering av alle input parametre
- Error Handling: Ingen lekkasje av interne detaljer
- Least Privilege: Minimal Graph API permissions
- Demo Mode: Sikker testing uten ekte Graph API tilgang
Anbefalte produksjonstiltak:
- Roter JWT signing keys regelmessig
- Bruk HTTPS i produksjon
- Implementer rate limiting per API key
- Monitor og log sikkerhetshendelser
- Konfigurer CORS restriktivt
# Build production image
docker build -t graphscopeproxy:latest .
# Run with environment file
docker run -d \
--name graphscopeproxy \
--env-file .env.production \
-p 8080:8080 \
graphscopeproxy:latest# Deploy to Azure Container Instances
az container create \
--resource-group your-rg \
--name graphscopeproxy \
--image your-registry/graphscopeproxy:latest \
--environment-variables \
GraphScope__TenantId="your-tenant" \
GraphScope__ClientId="your-client" \
--secure-environment-variables \
GraphScope__ClientSecret="your-secret" \
GraphScope__JwtSigningKey="your-key" \
--ports 8080✅ Implementert logging:
- Strukturerte JSON logs via Serilog
- Correlation IDs for request tracking
- Health check endpoint med Graph API status
- Error tracking og performance metrics
Log nivåer:
Information: Normal flow (login, proxy calls)Warning: Fallback til demo mode, tom grupperError: Graph API feil, auth feilDebug: Detaljert klassifisering og filtering
Health checks:
# Application health
curl http://localhost:5000/health
# Inkluderer:
# - Application status
# - Graph API connectivity (production mode)
# - Configuration validation✅ Komplett test suite implementert:
# Kjør alle tester
dotnet test
# Kjør med detaljert output
dotnet test --verbosity normal
# Test results
# ✅ Unit tests: 22/22 passerer
# ✅ GraphScopeProxy.Tests: Alle services og komponenter
# ⚠️ Integration tests: Struktur på plass, må utvidesTest kategorier:
- JWT Service tests (token generering, validering, invalidering)
- API Key Service tests (validation og gruppe-mapping)
- Resource Classifier tests (type detection, kapasitet, lokasjon)
- Graph API Service tests (mock og error handling)
- Controller tests (auth flow, proxy functionality)
- Opprett ny App Registration i Azure Portal
- Konfigurer API permissions:
- Microsoft Graph → Application permissions
Group.Read.All(Required - for gruppe-medlemmer)Places.Read.All(Optional - for Places API)
- Client Secret: Opprett og lagre hemmeligheten
- Grant admin consent for permissions
# Required settings
export GraphScope__TenantId="your-tenant-id"
export GraphScope__ClientId="your-client-id"
export GraphScope__ClientSecret="your-client-secret"
export GraphScope__JwtSigningKey="your-256-bit-secret-key"
# API Keys (format: key=group1,group2)
export ApiKeys__your-api-key="group-id-1,group-id-2"
# Optional settings
export GraphScope__JwtExpirationSeconds="900"
export GraphScope__MaxScopeSize="500"
export GraphScope__UseGraphPlacesApi="true"- ✅ Authentication: Login/logout/refresh med JWT
- ✅ Graph API Integration: Full Microsoft Graph SDK integrasjon
- ✅ Resource Classification: Automatisk type detection
- ✅ Proxy Functionality: Transparent Graph API proxying
- ✅ Response Filtering: Scope-basert response filtering
- ✅ Demo Mode: Testing uten ekte Graph API
- ✅ Testing: 22 unit tests passerer
- ✅ Docker: Container-ready deployment
- Rate Limiting: Per API key og globale grenser
- Redis Cache: Distribuert caching for scale-out
- Metrics: Prometheus/Grafana observability
- Admin API: Cache management og scope inspection
- Circuit Breaker: Resiliens mot Graph API outages
- Audit Logging: Compliance og sikkerhetssporing
- Current: Memory cache, enkelt instans
- Tested: 500 ressurser per scope (konfigurerbar)
- JWT Size: Kompakt med kun metadata
- Response Time: <100ms for cache hits
- Scalability: Stateless design, container-ready
Se LICENSE.
GraphScopeProxy er nå en fullstendig fungerende MVP!
For bidrag:
- Åpne issues for bugs eller feature requests
- Pull requests er velkomne med:
- Tester for nye features
- Oppdatert dokumentasjon
- Performance forbedringer
Utviklings setup:
git clone <repo> && cd GraphScopeProxy
dotnet restore
dotnet test
dotnet run --project src/GraphScopeProxy.Api🎉 Status: MVP implementert og klar for produksjon!
For detaljert arkitektur og design decisions, se modelplanning.md og dotnet-architecture.md.