Skip to content
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

Processing of claim mappings #140

Open
peterfarrell opened this issue Jan 25, 2021 · 6 comments
Open

Processing of claim mappings #140

peterfarrell opened this issue Jan 25, 2021 · 6 comments
Assignees

Comments

@peterfarrell
Copy link
Contributor

peterfarrell commented Jan 25, 2021

Wondering how best to handle this a situation where the claim data needs to be transformed into something useable. By default, Microsoft AD sends Object GUIDs (UUIDs) as base64 encoded strings in little-endian byte order.

In this example, this was the only place for use to convert / transform that GUID into something usable.

import base64
import uuid

from django_auth_adfs.backend import AdfsAccessTokenBackend


class CustomAdfsAccessTokenBackend(AdfsAccessTokenBackend):
    def validate_access_token(self, access_token):
        claims = super().validate_access_token(access_token=access_token)

        # Transform base64 objectGUID to a legit UUID
        if claims['objectGUID']:
            claims['objectGUID'] = uuid.UUID(bytes_le=base64.b64decode(claims['objectGUID']))

        return claims

This needs to be transformed / converted before create_user as it's needed by custom create_user method.

One idea is allow a person to set a callable on the mappings:

import uuid

def transform_objectguid(value):
        return uuid.UUID(bytes_le=base64.b64decode(value)

'CLAIM_MAPPING': {
    'first_name': 'FirstName',
    'last_name': 'LastName',
    'email': 'Email',
    'phone_number': 'TelephoneNumber',
    'ad_object_guid': {
        'name': 'objectGUID',
        'transform':  transform_objectguid
   },
}

Another idea is a post_validate_access_token_hook:

import uuid

def post_validate_access_token(claims):
        # Transform base64 objectGUID to real UUID
        if claims['objectGUID']:
            claims['objectGUID'] = uuid.UUID(bytes_le=base64.b64decode(claims['objectGUID']))

        return claims

'CLAIM_MAPPING': {
    'first_name': 'FirstName',
    'last_name': 'LastName',
    'email': 'Email',
    'phone_number': 'TelephoneNumber',
    'ad_object_guid':  'objectGUID'
}
'POST_VALIDATE_ACCESS_TOKEN_FUNCTION': post_validate_access_token  # or 'dot.path.to.function' takes claims dict
Fund with Polar
@JonasKs
Copy link
Member

JonasKs commented Jan 26, 2021

What's the use case for even storing the objectGUID ? 🤔

As for the implementations, I think a single setting such as POST_VALIDATE_ACCESS_TOKEN_FUNCTION would be preferred. It won't break backwards compatibility, easier to document and can do the same.

@peterfarrell
Copy link
Contributor Author

peterfarrell commented Jan 26, 2021

@JonasKs I can do a PR for the POST_VALIDATE_ACCESS_TOKEN_FUNCTION functionality.

The use case is being able to match up the objectGUID from a claim to a user that sync'ed into an application by a different system. The objectGUID is used to ensure the user sync'ed in from the other system matches the person authenticated. The objectGUID is the only stable identifier in AD for a user profile especially when there is potential for recycling of email addresses and/or usernames. DNs change when objects are moved and not a stable identifier.

From: https://docs.microsoft.com/en-us/windows/win32/ad/using-objectguid-to-bind-to-an-object

If an application stores or caches identifiers or references to objects stored in Active Directory Domain Services, the object GUID is the best identifier to use for several reasons:

  1. The objectGUID property of on object never changes even if the object is renamed or moved.
  2. It is easy to bind to the object using the object GUID.
  3. If the object is renamed or moved, the objectGUID property provides a single identifier that can be used to quickly find and identify the object rather than having to compose a query that has conditions for all properties that would identify that object.

@JonasKs
Copy link
Member

JonasKs commented Jan 26, 2021

I can do a PR for the POST_VALIDATE_ACCESS_TOKEN_FUNCTION functionality.

Awesome!

Thanks for explaining that to me. 🍻

@AUitto
Copy link

AUitto commented May 24, 2021

Hey, is there anything regarding this issue? I'm attempting to use oid in claim as the id for a newly created user, and I'm facing similar issue with the UUID.

@peterfarrell
Copy link
Contributor Author

@AUitto I haven't had time to implement a PR yet. Current workaround is to create custom backends. Then be sure to use your custom backends in your Settings.py instead of the ones provided by Django Auth ADFS directly.

import base64
import uuid
from django_auth_adfs.backend import AdfsAuthCodeBackend, AdfsAccessTokenBackend

class CustomAdfsAuthCodeBackend(AdfsAuthCodeBackend):
    def validate_access_token(self, access_token):
        claims = super().validate_access_token(access_token=access_token)

        # Transform base64 objectGUID to real UUID
        if claims['objectGUID']:
            claims['objectGUID'] = uuid.UUID(bytes_le=base64.b64decode(claims['objectGUID']))


class CustomAdfsAccessTokenBackend(AdfsAccessTokenBackend):
    def validate_access_token(self, access_token):
        claims = super().validate_access_token(access_token=access_token)

        # Transform base64 objectGUID to real UUID
        if claims['objectGUID']:
            claims['objectGUID'] = uuid.UUID(bytes_le=base64.b64decode(claims['objectGUID']))

        return claims

@AUitto
Copy link

AUitto commented May 24, 2021

Aight, thanks for a swift response. I'll have a look at the workaround.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants