Skip to content

Conversation

jmpesp
Copy link
Contributor

@jmpesp jmpesp commented Oct 9, 2025

Implement the CRUD routines for the tokens that will be used to authenticate SCIM clients for SamlScim Silos. Also implement the Bearer based authentication for SCIM clients.

Fill in the skeleton of CrdbScimProviderStore, which when implemented will complete the SCIM implementation in Nexus.

Implement the CRUD routines for the tokens that will be used to
authenticate SCIM clients for SamlScim Silos. Also implement the Bearer
based authentication for SCIM clients.

Fill in the skeleton of CrdbScimProviderStore, which when implemented
will complete the SCIM implementation in Nexus.
@jmpesp jmpesp requested a review from david-crespo October 9, 2025 02:56
Instead of silo

Also grant silo admin role to USER_TEST_PRIVILEGED.id() instead of
OpContext::for_tests in tests.
@david-crespo
Copy link
Contributor

I had a good conversation with the Codex CLI about the choice to effectively bypass the existing authn schemes setup here, treating these calls as "unauthed" from the point of view of the main authn system. The robot was pretty persuasive but I think the choice is worth pointing out and documenting in comments, probably on the scim_idp_get_provider.

https://gist.github.com/david-crespo/14d25cb770f9e10195f6d0ea45286874

id: Uuid::new_v4(),
time_created: Utc::now(),
time_deleted: None,
// TODO: allow setting an expiry? have a silo default?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mind filing a follow up ticket for this and noting it in the comment

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

opened #9260

use nexus_db_schema::schema::scim_client_bearer_token::dsl;
let maybe_token = dsl::scim_client_bearer_token
.filter(dsl::bearer_token.eq(bearer_token))
.filter(dsl::time_deleted.is_null())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need filters for where we are not past the expired time here and other places?


const BEARER: &str = "Bearer ";

if !token.starts_with(BEARER) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't remember where we landed on this but assuming this logic is correct we are going to require operators to use Bearer xxx-xxx-xxx-xxx on the IdP side rather than just a raw header value?

Also the logic could simply become:

        let Some(token) = token.strip_prefix(BEARER) else {
            return Err(Error::Unauthenticated {
                internal_message: "Invalid bearer token".to_string(),
            });
        };

Then that could be passed to scim_idp_lookup_token_by_bearer() directly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changed to Bearer oxide-scim-token in 3b3b891 to fit with how other authn schemes work - we'll have to include some docs about it.


assert!(tokens.is_empty());

// Fleet admins can create SCIM client tokens
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just going off of the comment here, but can we add a test that a non fleet admin cannot create a SCIM client token?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Silo admins should be able to create SCIM tokens too. I can add a test to the follow up PR (that creates the silo admin users) to test this too.

create opctx that uses that actor

create new authn scheme for "Bearer oxide-scim-...

make opctx.authorize calls make more sense

remove external-scim

create synthetic resource ScimClientBearerTokenList for tighter
permission structure:
- external-auth cannot list children on ScimClientBearerTokenList for
  example
Copy link
Contributor

@david-crespo david-crespo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still looking through, but here is a pile of comments. I am much happier with this after adding a formal authn scheme for scim.

/// might use a Silo. You usually want to use [`Context::silo_required()`]
/// if you don't expect to be looking at a built-in user.
///
/// Additionally, non-user Actors may also be associated with a Silo.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to say that by "non-user actor" you mean the SCIM arm of the match. It's obvious when you're looking at the diff but might not be so obvious later.

// XXX Ok, or an error?
return Ok(());
};

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it should error, that might break everything for no reason. Interestingly, the SCIM actor technically does have a role on the fleet, it just happens to be hard-coded. It probably doesn't make sense for this method to load that pseudo-assignment into roleset, though it does make me wonder what is doing that if not this method. I will poke around — no change requested here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I misunderstood this function - it's not inserting roles for a actor, it's looking them up, so this is a short circuit because SCIM actors won't ever have roles. 493af88 removes it

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be good to comment // short circuit for actors that won't ever have roles


authn::Actor::Scim { .. } => false,
}
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes me feel bad, but it might just be the name is_user, which, while it technically does divide the options the right way, doesn't feel like it names the meaning of the distinction. Or maybe the problem is that we're going to leave off is_user by mistake and cause a bug that way. If we see this extending to service accounts, are those going to also be non-users? Should they have read perms on their own silo? Maybe this is a question for later, but it illustrates the weirdness of this concept.

silo1-proj1-collaborator ✘ ✘ ✘ ✘ ✘ ✘ ✘ ✘
silo1-proj1-viewer ✘ ✘ ✘ ✘ ✘ ✘ ✘ ✘
unauthenticated ! ! ! ! ! ! ! !
scim ✘ ✘ ✘ ✘ ✘ ✘ ✘ ✘
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so good :)

let provider = self.scim_get_provider_from_opctx(opctx).await?;

let result = match provider.patch_group(&group_id, body).await {
Ok(response) => response.to_http_response(StatusCode::OK),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How come some of these take StatusCode::OK and some don't?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The scim2-rs::SingleResourceResponse is converted to a http::Response, but the call site needs to know the proper status code cause the conversion routine does not know for what HTTP method it's being called for. POST needs to be 201, and the rest need to be 200.

@jmpesp
Copy link
Contributor Author

jmpesp commented Oct 21, 2025

@papertigers the change to have fn silo_id return a silo for an Actor::Scim caused the test_iam_roles_behavior test to fail. I think this can be addressed with the Polar changes we were talking about?

@jmpesp jmpesp enabled auto-merge (squash) October 22, 2025 21:29
@jmpesp jmpesp merged commit af3b89f into oxidecomputer:main Oct 22, 2025
18 checks passed
@jmpesp jmpesp deleted the scim_token_crud_and_auth branch October 23, 2025 00:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants