Skip to content

Commit a790e11

Browse files
committed
Entra External Auth for ADO
1 parent 2cb9bfd commit a790e11

File tree

3 files changed

+49
-4
lines changed

3 files changed

+49
-4
lines changed

coderd/externalauth/externalauth.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,9 @@ func ConvertConfig(instrument *promoauth.Factory, entries []codersdk.ExternalAut
499499
if entry.Type == string(codersdk.EnhancedExternalAuthProviderAzureDevops) {
500500
oauthConfig = &jwtConfig{oc}
501501
}
502+
if entry.Type == string(codersdk.EnhancedExternalAuthProviderAzureDevopsEntra) {
503+
oauthConfig = &jwtConfigEntra{oc}
504+
}
502505
if entry.Type == string(codersdk.EnhancedExternalAuthProviderJFrog) {
503506
oauthConfig = &exchangeWithClientSecret{oc}
504507
}
@@ -705,6 +708,11 @@ var staticDefaults = map[codersdk.EnhancedExternalAuthProvider]codersdk.External
705708
Regex: `^(https?://)?dev\.azure\.com(/.*)?$`,
706709
Scopes: []string{"vso.code_write"},
707710
},
711+
codersdk.EnhancedExternalAuthProviderAzureDevopsEntra: {
712+
DisplayName: "Azure DevOps (Entra)",
713+
DisplayIcon: "/icon/azure-devops.svg",
714+
Regex: `^(https?://)?dev\.azure\.com(/.*)?$`,
715+
},
708716
codersdk.EnhancedExternalAuthProviderBitBucketCloud: {
709717
AuthURL: "https://bitbucket.org/site/oauth2/authorize",
710718
TokenURL: "https://bitbucket.org/site/oauth2/access_token",
@@ -777,6 +785,25 @@ func (c *jwtConfig) Exchange(ctx context.Context, code string, opts ...oauth2.Au
777785
)
778786
}
779787

788+
// When authenticating via Entra ID ADO only supports v1 tokens that requires the 'resource' rather than scopes
789+
type jwtConfigEntra struct {
790+
*oauth2.Config
791+
}
792+
793+
func (c *jwtConfigEntra) AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string {
794+
return c.Config.AuthCodeURL(state, append(opts, oauth2.SetAuthURLParam("resource", "499b84ac-1321-427f-aa17-267ca6975798"))...)
795+
}
796+
797+
func (c *jwtConfigEntra) Exchange(ctx context.Context, code string, opts ...oauth2.AuthCodeOption) (*oauth2.Token, error) {
798+
return c.Config.Exchange(ctx, code,
799+
append(opts,
800+
oauth2.SetAuthURLParam("client_id", c.ClientID),
801+
oauth2.SetAuthURLParam("resource", "499b84ac-1321-427f-aa17-267ca6975798"),
802+
oauth2.SetAuthURLParam("client_secret", c.ClientSecret),
803+
)...,
804+
)
805+
}
806+
780807
// exchangeWithClientSecret wraps an OAuth config and adds the client secret
781808
// to the Exchange request as a Bearer header. This is used by JFrog Artifactory.
782809
type exchangeWithClientSecret struct {

codersdk/externalauth.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,19 @@ func (e EnhancedExternalAuthProvider) Git() bool {
2424
EnhancedExternalAuthProviderGitLab,
2525
EnhancedExternalAuthProviderBitBucketCloud,
2626
EnhancedExternalAuthProviderBitBucketServer,
27-
EnhancedExternalAuthProviderAzureDevops:
27+
EnhancedExternalAuthProviderAzureDevops,
28+
EnhancedExternalAuthProviderAzureDevopsEntra:
2829
return true
2930
default:
3031
return false
3132
}
3233
}
3334

3435
const (
35-
EnhancedExternalAuthProviderAzureDevops EnhancedExternalAuthProvider = "azure-devops"
36-
EnhancedExternalAuthProviderGitHub EnhancedExternalAuthProvider = "github"
37-
EnhancedExternalAuthProviderGitLab EnhancedExternalAuthProvider = "gitlab"
36+
EnhancedExternalAuthProviderAzureDevops EnhancedExternalAuthProvider = "azure-devops"
37+
EnhancedExternalAuthProviderAzureDevopsEntra EnhancedExternalAuthProvider = "azure-devops-entra"
38+
EnhancedExternalAuthProviderGitHub EnhancedExternalAuthProvider = "github"
39+
EnhancedExternalAuthProviderGitLab EnhancedExternalAuthProvider = "gitlab"
3840
// EnhancedExternalAuthProviderBitBucketCloud is the Bitbucket Cloud provider.
3941
// Not to be confused with the self-hosted 'EnhancedExternalAuthProviderBitBucketServer'
4042
EnhancedExternalAuthProviderBitBucketCloud EnhancedExternalAuthProvider = "bitbucket-cloud"

docs/admin/external-auth.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ application. The following providers are supported:
2323
- [GitLab](https://docs.gitlab.com/ee/integration/oauth_provider.html)
2424
- [BitBucket](https://support.atlassian.com/bitbucket-cloud/docs/use-oauth-on-bitbucket-cloud/)
2525
- [Azure DevOps](https://learn.microsoft.com/en-us/azure/devops/integrate/get-started/authentication/oauth?view=azure-devops)
26+
- [Azure DevOps (via Entra ID)](https://learn.microsoft.com/en-us/entra/architecture/auth-oauth2)
2627

2728
Example callback URL:
2829
`https://coder.example.com/external-auth/primary-github/callback`. Use an
@@ -108,6 +109,21 @@ CODER_EXTERNAL_AUTH_0_AUTH_URL="https://app.vssps.visualstudio.com/oauth2/author
108109
CODER_EXTERNAL_AUTH_0_TOKEN_URL="https://app.vssps.visualstudio.com/oauth2/token"
109110
```
110111

112+
### Azure DevOps (via Entra ID)
113+
114+
Azure DevOps (via Entra ID) requires the following environment variables:
115+
116+
```env
117+
CODER_EXTERNAL_AUTH_0_ID="primary-azure-devops"
118+
CODER_EXTERNAL_AUTH_0_TYPE=azure-devops-entra
119+
CODER_EXTERNAL_AUTH_0_CLIENT_ID=xxxxxx
120+
CODER_EXTERNAL_AUTH_0_CLIENT_SECRET=xxxxxxx
121+
CODER_EXTERNAL_AUTH_0_AUTH_URL="https://login.microsoftonline.com/<TENANT ID>/oauth2/authorize"
122+
CODER_EXTERNAL_AUTH_0_TOKEN_URL="https://login.microsoftonline.com/<TENANT ID>/oauth2/token"
123+
```
124+
125+
> Note: Your app registration in Entra ID requires the `vso.code_write` scope
126+
111127
### Self-managed git providers
112128

113129
Custom authentication and token URLs should be used for self-managed Git

0 commit comments

Comments
 (0)