├── .github └── workflows │ ├── LICENSE.md │ ├── update.yml │ ├── archive.yml │ ├── ghpages.yml │ └── publish.yml ├── LICENSE.md ├── .editorconfig ├── .gitignore ├── Makefile ├── README.md ├── CONTRIBUTING.md └── draft-ipsie-scim-al1-profile.md /.github/workflows/LICENSE.md: -------------------------------------------------------------------------------- 1 | This project is in the public domain. 2 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | See the 4 | [guidelines for contributions](https://github.com/openid/ipsie-scim-al1/blob/main/CONTRIBUTING.md). 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # See http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*.{md,xml,org}] 6 | charset = utf-8 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.pdf 3 | *.redxml 4 | *.swp 5 | *.txt 6 | *.upload 7 | *~ 8 | .tags 9 | /*-[0-9][0-9].xml 10 | /.*.mk 11 | /.gems/ 12 | /.refcache 13 | /.venv/ 14 | /.vscode/ 15 | /lib 16 | /node_modules/ 17 | /versioned/ 18 | Gemfile.lock 19 | archive.json 20 | draft-ipsie-scim-al1-profile.xml 21 | package-lock.json 22 | report.xml 23 | !requirements.txt 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LIBDIR := lib 2 | include $(LIBDIR)/main.mk 3 | 4 | $(LIBDIR)/main.mk: 5 | ifneq (,$(shell grep "path *= *$(LIBDIR)" .gitmodules 2>/dev/null)) 6 | git submodule sync 7 | git submodule update --init 8 | else 9 | ifneq (,$(wildcard $(ID_TEMPLATE_HOME))) 10 | ln -s "$(ID_TEMPLATE_HOME)" $(LIBDIR) 11 | else 12 | git clone -q --depth 10 -b main \ 13 | https://github.com/martinthomson/i-d-template $(LIBDIR) 14 | endif 15 | endif 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IPSIE AL1 SCIM 2.0 Profile 2 | 3 | This is the working area for the IPSIE Working Group draft, "IPSIE AL1 SCIM 2.0 Profile". 4 | 5 | * [Editor's Copy](https://openid.github.io/ipsie-scim-al1/draft-ipsie-scim-al1-profile.html) 6 | 7 | 8 | ## Contributing 9 | 10 | See the 11 | [guidelines for contributions](https://github.com/openid/ipsie-scim-al1/blob/main/CONTRIBUTING.md). 12 | 13 | Contributions can be made by creating pull requests. 14 | The GitHub interface supports creating pull requests using the Edit (✏) button. 15 | 16 | -------------------------------------------------------------------------------- /.github/workflows/update.yml: -------------------------------------------------------------------------------- 1 | name: "Update Generated Files" 2 | # This rule is not run automatically. 3 | # It can be run manually to update all of the files that are part 4 | # of the template, specifically: 5 | # - README.md 6 | # - CONTRIBUTING.md 7 | # - .note.xml 8 | # - .github/CODEOWNERS 9 | # - Makefile 10 | # 11 | # 12 | # This might be useful if you have: 13 | # - added, removed, or renamed drafts (including after adoption) 14 | # - added, removed, or changed draft editors 15 | # - changed the title of drafts 16 | # 17 | # Note that this removes any customizations you have made to 18 | # the affected files. 19 | on: workflow_dispatch 20 | 21 | jobs: 22 | build: 23 | name: "Update Files" 24 | runs-on: ubuntu-latest 25 | steps: 26 | - name: "Checkout" 27 | uses: actions/checkout@v4 28 | 29 | - name: "Update Generated Files" 30 | uses: martinthomson/i-d-template@v1 31 | with: 32 | make: update-files 33 | token: ${{ github.token }} 34 | 35 | - name: "Push Update" 36 | run: git push 37 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | The OpenID Foundation IPSIE working group page is https://openid.net/wg/ipsie/. It describes how to participate in the working groups including IPSIE. 4 | 5 | You can send feedback on the specifications in a way that enables the working group to act upon it by 6 | 7 | 1. signing the contribution agreement at https://openid.net/intellectual-property/ to join the working group (please specify that you are joining the IPSIE working group on your contribution agreement), 8 | 2. joining the working group mailing list at https://lists.openid.net/mailman/listinfo/openid-specs-ipsie/, and 9 | 3. sending your feedback to the list. 10 | 11 | Working group members can also contribute via GitHub. 12 | 13 | When contributing, please adhere to the following guidelines: 14 | 15 | - **Issues**: Use the issue tracker to report problems or suggest enhancements. 16 | - **Pull Requests**: Submit pull requests linked to Issues that were approved during a Working Group call. 17 | - **Commit Messages**: Use clear and descriptive commit messages. 18 | -------------------------------------------------------------------------------- /.github/workflows/archive.yml: -------------------------------------------------------------------------------- 1 | name: "Archive Issues and Pull Requests" 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * 0,2,4' 6 | repository_dispatch: 7 | types: [archive] 8 | workflow_dispatch: 9 | inputs: 10 | archive_full: 11 | description: 'Recreate the archive from scratch' 12 | default: false 13 | type: boolean 14 | 15 | jobs: 16 | build: 17 | name: "Archive Issues and Pull Requests" 18 | runs-on: ubuntu-latest 19 | permissions: 20 | contents: write 21 | steps: 22 | - name: "Checkout" 23 | uses: actions/checkout@v4 24 | 25 | # Note: No caching for this build! 26 | 27 | - name: "Update Archive" 28 | uses: martinthomson/i-d-template@v1 29 | env: 30 | ARCHIVE_FULL: ${{ inputs.archive_full }} 31 | with: 32 | make: archive 33 | token: ${{ github.token }} 34 | 35 | - name: "Update GitHub Pages" 36 | uses: martinthomson/i-d-template@v1 37 | with: 38 | make: gh-archive 39 | token: ${{ github.token }} 40 | 41 | - name: "Save Archive" 42 | uses: actions/upload-artifact@v4 43 | with: 44 | path: archive.json 45 | -------------------------------------------------------------------------------- /.github/workflows/ghpages.yml: -------------------------------------------------------------------------------- 1 | name: "Update Editor's Copy" 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - README.md 7 | - CONTRIBUTING.md 8 | - LICENSE.md 9 | - .gitignore 10 | pull_request: 11 | paths-ignore: 12 | - README.md 13 | - CONTRIBUTING.md 14 | - LICENSE.md 15 | - .gitignore 16 | 17 | jobs: 18 | build: 19 | name: "Update Editor's Copy" 20 | runs-on: ubuntu-latest 21 | permissions: 22 | contents: write 23 | steps: 24 | - name: "Checkout" 25 | uses: actions/checkout@v4 26 | 27 | - name: "Setup" 28 | id: setup 29 | run: date -u "+date=%FT%T" >>"$GITHUB_OUTPUT" 30 | 31 | - name: "Caching" 32 | uses: actions/cache@v4 33 | with: 34 | path: | 35 | .refcache 36 | .venv 37 | .gems 38 | node_modules 39 | .targets.mk 40 | key: i-d-${{ steps.setup.outputs.date }} 41 | restore-keys: i-d- 42 | 43 | - name: "Build Drafts" 44 | uses: martinthomson/i-d-template@v1 45 | with: 46 | token: ${{ github.token }} 47 | 48 | - name: "Update GitHub Pages" 49 | uses: martinthomson/i-d-template@v1 50 | if: ${{ github.event_name == 'push' }} 51 | with: 52 | make: gh-pages 53 | token: ${{ github.token }} 54 | 55 | - name: "Archive Built Drafts" 56 | uses: actions/upload-artifact@v4 57 | with: 58 | path: | 59 | draft-*.html 60 | draft-*.txt 61 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: "Publish New Draft Version" 2 | 3 | on: 4 | push: 5 | tags: 6 | - "draft-*" 7 | workflow_dispatch: 8 | inputs: 9 | email: 10 | description: "Submitter email" 11 | default: "" 12 | type: string 13 | 14 | jobs: 15 | build: 16 | name: "Publish New Draft Version" 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: "Checkout" 20 | uses: actions/checkout@v4 21 | 22 | # See https://github.com/actions/checkout/issues/290 23 | - name: "Get Tag Annotations" 24 | run: git fetch -f origin ${{ github.ref }}:${{ github.ref }} 25 | 26 | - name: "Setup" 27 | id: setup 28 | run: date -u "+date=%FT%T" >>"$GITHUB_OUTPUT" 29 | 30 | - name: "Caching" 31 | uses: actions/cache@v4 32 | with: 33 | path: | 34 | .refcache 35 | .venv 36 | .gems 37 | node_modules 38 | .targets.mk 39 | key: i-d-${{ steps.setup.outputs.date }} 40 | restore-keys: i-d- 41 | 42 | - name: "Build Drafts" 43 | uses: martinthomson/i-d-template@v1 44 | with: 45 | token: ${{ github.token }} 46 | 47 | - name: "Upload to Datatracker" 48 | uses: martinthomson/i-d-template@v1 49 | with: 50 | make: upload 51 | env: 52 | UPLOAD_EMAIL: ${{ inputs.email }} 53 | 54 | - name: "Archive Submitted Drafts" 55 | uses: actions/upload-artifact@v4 56 | with: 57 | path: "versioned/draft-*-[0-9][0-9].*" 58 | -------------------------------------------------------------------------------- /draft-ipsie-scim-al1-profile.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "IPSIE AL1 SCIM 2.0 Profile" 3 | abbrev: "IPSIE AL1 SCIM" 4 | category: info 5 | 6 | docname: draft-ipsie-scim-al1-profile-latest 7 | submissiontype: "independent" 8 | number: 9 | date: 10 | v: 1 11 | workgroup: IPSIE Working Group 12 | keyword: 13 | - scim 14 | - ipsie 15 | venue: 16 | group: IPSIE 17 | type: Working Group 18 | mail: openid-specs-ipsie@lists.openid.net 19 | arch: https://openid.net/wg/ipsie/ 20 | github: "openid/ipsie-scim-al1" 21 | latest: "https://openid.github.io/ipsie-scim-al1/draft-ipsie-scim-al1-profile.html" 22 | 23 | author: 24 | - 25 | fullname: Mark Maguire 26 | organization: Aujas Cybersecurity 27 | email: mark.maguire@aujas.com 28 | - 29 | fullname: Jen Schreiber 30 | organization: SGNL 31 | email: jen@sgnl.ai 32 | 33 | normative: 34 | RFC8174: 35 | RFC2119: 36 | RFC7523: 37 | RFC7643: 38 | RFC7644: 39 | RFC6749: 40 | RFC8414: 41 | 42 | --- abstract 43 | 44 | This document defines a profile for SCIM 2.0 to meet the security and interoperability requirements for identity lifecycle management within enterprises. Within the context of SCIM, The profile establishes requirements for provisioning, account management, client authentication, and identity synchronization. 45 | 46 | --- middle 47 | 48 | # Introduction 49 | 50 | This document defines the IPSIE Account Lifecycle 1 (AL1) Profile for SCIM 2.0. It provides a clear reference for SCIM deployments that require a well-defined security baseline meeting best practices for interoperable enterprise identity management. 51 | 52 | The profile addresses critical aspects of secure identity management, with particular emphasis on: 53 | 54 | * Client authentication 55 | * Retrieve, add, and modify Users. 56 | * Retrieve, add, and modify Groups. 57 | * Synchronization of data from the Identity Service to the Application 58 | 59 | By adhering to this profile, organizations can implement SCIM-based integrations that meet stringent security requirements while ensuring interoperability across different implementations. 60 | 61 | # Conventions and Definitions 62 | 63 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here. 64 | 65 | ## Terminology 66 | 67 | SCIM 68 | 69 | > The System for Cross-domain Identity Management as defined in {{RFC7643}} and {{RFC7644}} 70 | 71 | SCIM Client 72 | 73 | > An application that uses the SCIM protocol to manage identity data maintained by the service provider. The client initiates SCIM HTTP requests to 74 | a target service provider. 75 | 76 | SCIM Service Provider 77 | 78 | > An HTTP web application that provides identity information via the SCIM protocol. 79 | 80 | Role 81 | 82 | > TODO: Add definition 83 | 84 | Identity Service 85 | 86 | > Acts as the SCIM client, initiating all provisioning operations. 87 | 88 | Application 89 | 90 | > Acts as the SCIM service provider, hosting SCIM endpoints and processing all provisioning requests. 91 | 92 | Note: When SCIM is applied to the context of IPISIE, the Identity Service acts as the SCIM client and the Application acts as the SCIM service provider. The document will use the Role terms below for consistency between across IPSIE Profiles. 93 | 94 | # Profile 95 | 96 | ## Authentication and Authorization {#authn-authz} 97 | 98 | The Identity Service and Application MUST use OAuth 2.0 {{RFC6749}} for authentication and authorization of SCIM protocol. 99 | 100 | // TODO: Should this link back to SL1? 101 | 102 | // TODO: Expand Section 103 | 104 | The following requirements ensure consistent and secure handling of access tokens and authorization server configuration: 105 | 106 | // TODO: Be more explicit with token names. 107 | 108 | * OAuth 2.0 interactions MUST comply with JWT Client Authentication as defined in {{RFC7523}} 109 | * The token SHALL exchange a signed JWT for an access token and present that token in the {Authorization: Bearer} header on all subsequent SCIM requests. 110 | * The token MUST contain a "token_endpoint" value which is the URL of the Identity Service's OAuth 2.0 token endpoint. 111 | * The Acess Token MUST include the "scim" scope and not grant broader permissions. 112 | * All Authorization Server parameters SHOULD be discovered from OAuth Authorization Server metadata as defined in {{RFC8414}}. 113 | * The Identity Service SHOULD expose a jwks_uri to allow the Application to perform signature verification 114 | 115 | ## SCIM Interoperability Requirements 116 | 117 | ### General Requirements 118 | 119 | * The Identity Service SHALL implement the required functionality of a SCIM client as defined in {{RFC7643}} and {{RFC7644}}. 120 | * The Application SHALL implement the required functionality of a SCIM service provider as defined in {{RFC7643}} and {{RFC7644}}. 121 | * All SCIM operations SHALL be authenticated and authorized via OAuth 2.0 as specified in {{authn-authz}}. 122 | * Local modifications to Users or Groups in the Application are prohibited. 123 | 124 | ### User Provisioning Operations 125 | 126 | The Application MUST provide support all User provisioning operations defined in this section. 127 | 128 | #### Create User (POST /Users) 129 | 130 | User creation is performed by the SCIM operation POST /Users. 131 | 132 | In addition to the user attributes required by {{RFC7643}}, the following attributes are required to be part of the User schema: 133 | 134 | // TODO: Should we keep this vauge or refer be explicit with attributes we are requiring? email vs emails? 135 | 136 | * An attribute which contains a unique identifier used by the enterprise to distinguish the owner of the account, such as "externalId." 137 | * An attribute which contains the primary email address of the user, such as "email" 138 | 139 | #### Update User (PATCH /Users/{id}) 140 | 141 | User updates are performed by the SCIM operation PATCH /Users/{id} 142 | 143 | #### Deactivate or Reactivate User (PATCH /Users/{id}) 144 | 145 | Changes to the user activation status, such deactivation and reactivation, are performed by the SCIM operation PATCH /Users/{id} 146 | 147 | The Identity Service SHOULD propagate user deactivation events to the Application within 5 minutes of the user being deactivated. 148 | 149 | The Application SHOULD respond to user deactivation events by revoking the ability for the user to continue accessing the Application, including the revocation of currently active sessions. The revocation mechanism outside the scope of this profile. Revocation SHOULD occur within 5 minutes of receiving the deactivation request. 150 | 151 | When a user account is deactivated, all access mechanisms and authorizations associated with that account MUST also be deactivated. This includes, but is not limited to: 152 | 153 | * Web sessions 154 | * API tokens 155 | * Refresh tokens 156 | * Personal access tokens 157 | * SSH keys associated with the user 158 | * Device-based authentication credentials 159 | 160 | The Application MUST allow reactivation of a deactivated user. 161 | 162 | #### Delete User (DELETE /Users/{id}) 163 | 164 | User deletions are performed by the SCIM operation DELETE /Users/{id} 165 | 166 | After a user is deleted, the Application MUST allow the creation of a new user with the same username. 167 | 168 | // TODO: this could be tricky RE: maintianing the user's data 169 | 170 | #### Get All Users (GET /Users) 171 | 172 | Get all users in the system is performed by the SCIM operation GET /Users 173 | 174 | This endpoint ensures that all users are managed by the Identity Service. 175 | 176 | To ensure that large amounts of data can be read from the Application, the application must support with index-based or cursor-based pagination for the GET /Users request. To ensure system stability and prevent abuse, the Application SHALL enforce rate limits on this endpoint and must respond with appropriate headers, such as "429 Too Many Requests" and "Retry-After," when limits are exceeded. 177 | 178 | #### Get User By ID (GET /Users/{id}) 179 | 180 | User searches by id are performed by the SCIM operation GET /Users/{id} 181 | 182 | #### List Users By Alternate Identifier (GET /Users?) 183 | 184 | User searches by alternate identifier are performed via the SCIM operation: GET /Users?filter={filterExpression} 185 | 186 | Application Providers MUST support the following filter expressions: 187 | 188 | * username eq \{username\} 189 | * externalId eq \{externalId\} 190 | * emails[value eq \{email\}] 191 | 192 | ### Group (Role) Provisioning Operations 193 | 194 | The Application MUST provide support all Group provisioning operations defined in this section. 195 | 196 | **Note**: Within the IPSIE standard, Application permissions are referred to as "Roles." Within SCIM, Application permissions are referred to as "Groups." The term "Role" in IPSIE is functionally equivalent to the term "Group" in SCIM. 197 | 198 | #### Create Group (POST /Groups) 199 | 200 | Group creation is performed by the SCIM operation POST /Group. 201 | 202 | // TODO: Add more details 203 | 204 | #### Get All Groups (GET /Groups) 205 | 206 | A search for all groups in the system is performed by the SCIM operation GET /Groups 207 | 208 | This endpoint ensures that all groups are managed by the Identity Service. 209 | 210 | To ensure that large amounts of data can be read from the Application, the application MUST support with index-based or cursor-based pagination for the GET /Groups request. To ensure system stability and prevent abuse, the Application SHALL enforce rate limits on this endpoint and MUST respond with appropriate headers, such as "429 Too Many Requests" and "Retry-After," when limits are exceeded. 211 | 212 | #### Get Group By ID (GET /Group/{id}) 213 | 214 | Group searches by id are performed by the SCIM operation GET /Group/{id}?excludedAttributes=members 215 | 216 | #### List Groups By Alternate Identifier (GET /Groups?) 217 | 218 | User lookups by alternate identifier are performed by the SCIM operation GET /Groups?filter={filterExpression}&excludedAttributes=members 219 | 220 | Application Providers MUST support the following filter expressions: 221 | 222 | // TODO: Add what filters are required to be supported 223 | 224 | #### Add or Remove Group Members (PATCH /Group/{id}) 225 | 226 | Members are added or removed from Groups via the SCIM operation PATCH /Groups/{id} 227 | 228 | For each Operation in the PATCH: 229 | 230 | The op attribute MUST contain either "add" or "remove". 231 | 232 | * When the op is "add": 233 | * The path attribute MUST be "members". 234 | * The value attribute MUST be an array of Group member elements, as defined in Section 4.2 of the SCIM Core Schema {{RFC7643}}. Each member MUST contain a value subattribute with the id of the resource being added to the group. 235 | 236 | * When the op is "remove": 237 | * The path attribute MUST be either: 238 | * "members" (to remove all members) 239 | * "members[value eq \{id\}]" (to remove a single member) 240 | * The value attribute MUST be unspecified. 241 | 242 | ## Metadata Endpoints 243 | 244 | ### ResourceTypes 245 | 246 | Application MUST host a /ResourceTypes endpoint, as defined in Section 4 of {{RFC7644}}. 247 | 248 | The supported ResourceTypes MUST include Users and Groups. 249 | 250 | ### ServiceProviderConfig 251 | 252 | Application Providers MUST host a /ServiceProviderConfig endpoint to describe the operations they support, as defined in Section 4 of {{RFC7644}} 253 | 254 | The operations MUST include, at minimum, the set of SCIM capabilities required for compatibility with this IPSIE profile. 255 | 256 | ### Schemas 257 | 258 | Application Providers MUST host a /Schemas endpoint to describe the supported schemas, as defined in Section 4 of {{RFC7644}}. There must be a schema for both Users and Groups. The schemas must include all required attributes from RFC 7643 and from Section 3.2.3 (Create User). 259 | 260 | ## Security Considerations 261 | 262 | For SCIM security considerations, see {{RFC7643}} and {{RFC7644}} 263 | 264 | Additionally, the following requierements are included to address security considerations. 265 | 266 | * **Transport Security**: All endpoints SHALL enforce TLS 1.2 or later with strong cipher suites and certificate validation. 267 | * **Error Handling**: Error responses SHALL use the SCIM error format and SHALL NOT leak internal details. 268 | * **Replay Resistance**: Access tokens SHALL expire and nonces SHALL be validated to prevent replay. 269 | * **Auditing**: All provisioning actions and responses SHALL be logged for audit and troubleshooting. 270 | * **Rate Limiting**: All endpoints SHALL enforce rate limiting and must respond with "429 Too Many Requests" when limits are exceeded. 271 | 272 | # Compliance Statement 273 | 274 | Implementation of all mandatory requirements in this profile will result in a SCIM 2.0 deployment that satisfies IPSIE Identity Lifecycle Level 1 (IL1). Specifically: 275 | 276 | * **Identity Service (SCIM client)** 277 | * SHALL initiate all CRUD operations for Users and Groups. 278 | * SHALL adhere to the security considerations above. 279 | 280 | * **Application (SCIM service provider)** 281 | * SHALL host all SCIM endpoints with full support for User and Group provisioning. 282 | * SHALL prevent local modifications outside of SCIM. 283 | * SHALL enforce OAuth 2.0 JWT Profile for Authentication {{RFC7523}}. 284 | * SHALL adhere to the security considerations above. 285 | 286 | By conforming to this profile, implementations will achieve a consistent, secure, and interoperable baseline for enterprise identity lifecycle management. 287 | --------------------------------------------------------------------------------