-
Notifications
You must be signed in to change notification settings - Fork 902
feat: add support for Entra ID auth when using Azure DevOps external auth #12201
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
CLA Assistant Lite bot All contributors have signed the CLA ✍️ ✅ |
I have read the CLA Document and I hereby sign the CLA |
4a74886
to
ba00100
Compare
Good news, I got it working! Turns out ADO doesn't support V2 of the Entra ID tokens so I had to switch from these endpoints:
To these endpoints
In this configuration rather than specifying a scope you specify the GUID of the ADO resource and that gives you whatever scopes you have set in your app registration (still only requires I'm now going to look at making this its own provider to go alongside the existing ADO connection |
ba00100
to
a790e11
Compare
a790e11
to
24e4c97
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Appreciate the contribution! I will have to do some quick reading on this, but it all is looking pretty good.
I will allocate some time today to look this over.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see the PoC part now. I did not notice the resource
was a constant uuid.
@alexwilcox9 is it alright if I just build off your commits here?
Hey, thanks for having a look! |
coderd/externalauth/externalauth.go
Outdated
// When authenticating via Entra ID ADO only supports v1 tokens that requires the 'resource' rather than scopes | ||
type jwtConfigEntra struct { | ||
*oauth2.Config | ||
} | ||
|
||
func (c *jwtConfigEntra) AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string { | ||
return c.Config.AuthCodeURL(state, append(opts, oauth2.SetAuthURLParam("resource", "499b84ac-1321-427f-aa17-267ca6975798"))...) | ||
} | ||
|
||
func (c *jwtConfigEntra) Exchange(ctx context.Context, code string, opts ...oauth2.AuthCodeOption) (*oauth2.Token, error) { | ||
return c.Config.Exchange(ctx, code, | ||
append(opts, | ||
oauth2.SetAuthURLParam("client_id", c.ClientID), | ||
oauth2.SetAuthURLParam("resource", "499b84ac-1321-427f-aa17-267ca6975798"), | ||
oauth2.SetAuthURLParam("client_secret", c.ClientSecret), | ||
)..., | ||
) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@alexwilcox9 I am curious why we need the jwtConfigEntra
? By default client_secret
and client_id
are added to the requests, so the only additional field here is resource
.
Update the SCOPE configuration variable to "499b84ac-1321-427f-aa17-267ca6975798/.default" to refer to the Azure DevOps resource and all of its scopes.
It appears we can send this as a scope: export CODER_EXTERNAL_AUTH_2_SCOPES="vso.code_write,499b84ac-1321-427f-aa17-267ca6975798/.default"
I am having issues with my setup, so I don't have the resources to test this at present. Can you attempt to configure this without jwtConfigEntra
just using the SCOPE
env var I provided above?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @Emyrk
When I try and remove these functions and just set the scope I start getting the wrong audience in my tokens. It defaults to MS Graph because the resource isn't set
Happy to try some more configurations if you have more ideas as it'd be nice to get rid of this extra function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it, if you are able to get it to work as I suggested with the scopes rather than the resource
lmk.
Their docs are not the easiest to follow I find. If we can get this to work without the extra jwt struct, we can still pull in the sane defaults for entra.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right about the docs not being easy to follow.. I've been trying to find something official that says ADO only supports V1 tokens (which aren't the standard OIDC) but all I've got is the original Stack Overflow post that got me working in the first place
https://stackoverflow.com/questions/53788182/how-to-get-valid-aad-v2-token-using-msal-js-for-azure-devops
It looks like Azure DevOps is a v1.0 application
Then I found this article from Microsoft about the differences between v1 and v2 that shows resource
is now scopes
https://learn.microsoft.com/en-us/previous-versions/azure/active-directory/azuread-dev/azure-ad-endpoint-comparison#scopes-not-resources
I haven't been able to find the official spec for the v1 authorize endpoint that would tell us if there was a valid way to use scopes
Hopefully ADO gets support for v2 tokens and this extra struct and functions can be removed.
I've removed the client_secret
and client_id
like you suggested and it still works so we've got that.
Is having this extra struct a blocker for getting this functionality?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The extra struct is not a blocker, I just want to make sure it is necessary.
That stackoverflow link has an answer with this scope: 499b84ac-1321-427f-aa17-267ca6975798/user_impersonation
. Worth trying that too?
To confirm though, with the struct everything is working for you? And this is specifically Entra + ADO? (I'm not very familiar with all microsoft's offerings). We should rename the struct to something that references the V1 api from Entra.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just tried setting the scope to 499b84ac-1321-427f-aa17-267ca6975798/user_impersonation
and commented out the new functions and sadly it didn't work. The audience defaults back to MS Graph and the token isn't any good for ADO
I'll rename the struct/functions now to include V1 in the name and add a comment noting that it can be removed once ADO gets support for V2 tokens
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunate 😢
Can you cherry-pick this commit I made to make this dynamic?
And do you know any good ValidateURL we could select?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I couldn't get cherry pick to work so just copied your code over, I'll have a google and see if I can find a validate URL
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Np on the code copy. Working across origins is a bit annoying for git actions.
83ef463
to
e12e6e3
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can get in your approach 👍.
If in the future we figure out a way around this, or they implement the v2 apis, we can adjust from there.
CODER_EXTERNAL_AUTH_0_TOKEN_URL="https://login.microsoftonline.com/<TENANT ID>/oauth2/token" | ||
``` | ||
|
||
> Note: Your app registration in Entra ID requires the `vso.code_write` scope |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we make this a default scope? I made it one in my commit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what you mean by this? Are you saying replace vso.code_write
with a default scope?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just checked, I did not make it a default scope in my commit. Whoops!
If this scope should always be set, we can include it in the defaults.
defaults := codersdk.ExternalAuthConfig{
DisplayName: "Azure DevOps (Entra)",
DisplayIcon: "/icon/azure-devops.svg",
Regex: `^(https?://)?dev\.azure\.com(/.*)?$`,
Scopes: []string{"vso.code_write"},
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh! Understood, so there are no default scope recommendations on the Coder end?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's right, as I understand it we just request the 'resource' and receive a token with all scopes that have been set in the app registration
1d8ce22
to
be68ebd
Compare
be68ebd
to
bb87f0a
Compare
I had a dig into the endpoints to see if there was anything obvious we could use the validate that the token is working and so far all the endpoints that only require the Alternatively the profile endpoint doesn't require that knowledge but does require an additional scope ( I hope that's useful! I'm about to go on holiday for a week so won't be getting back to this until w/c 11th March |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approving this 👍
Would be nice to find a validate url in the future, but that is not required.
Very much a work in progress at the moment to prove the concept but I've hit a barrier and perhaps you'll be able to help.
To set a bit of context there are a few reasons I think Coder should support this method of authenticating to Azure DevOps:
So far I've just been editing the existing ADO connector, I appreciate this would need to be an additional connector to be merged as to not break anyone's existing deployments.
Entra app registration manifest
Signing in seems to work and I get a JWT back but the git clone operation always fails with
fatal: Authentication failed for...
Any tips would be very welcome