diff --git a/djangoldp/migrations/0002_auto_20190905_1542.py b/djangoldp/migrations/0002_auto_20190906_0642.py
similarity index 60%
rename from djangoldp/migrations/0002_auto_20190905_1542.py
rename to djangoldp/migrations/0002_auto_20190906_0642.py
index ff3b2dc14e84c5e2037fbd444450f3f668d86c29..c68a1c5147a9f96426e114d4da02f3e92f01e5dd 100644
--- a/djangoldp/migrations/0002_auto_20190905_1542.py
+++ b/djangoldp/migrations/0002_auto_20190906_0642.py
@@ -1,8 +1,9 @@
 # -*- coding: utf-8 -*-
-# Generated by Django 1.11 on 2019-09-05 15:42
+# Generated by Django 1.11 on 2019-09-06 06:42
 from __future__ import unicode_literals
 
-from django.db import migrations, models
+from django.db import migrations
+import djangoldp.fields
 
 
 class Migration(migrations.Migration):
@@ -16,9 +17,9 @@ class Migration(migrations.Migration):
             model_name='ldpsource',
             name='container',
         ),
-        migrations.AlterField(
+        migrations.AddField(
             model_name='ldpsource',
-            name='id',
-            field=models.URLField(primary_key=True, serialize=False),
+            name='urlid',
+            field=djangoldp.fields.LDPUrlField(null=True, unique=True),
         ),
     ]
diff --git a/djangoldp/migrations/0003_auto_20190911_0931.py b/djangoldp/migrations/0003_auto_20190911_0931.py
new file mode 100644
index 0000000000000000000000000000000000000000..72aa15bfbea5ed5f146cec65899bd9632fc47dcb
--- /dev/null
+++ b/djangoldp/migrations/0003_auto_20190911_0931.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11 on 2019-09-11 09:31
+from __future__ import unicode_literals
+
+from django.db import migrations
+import djangoldp.fields
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('djangoldp', '0002_auto_20190906_0642'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='ldpsource',
+            name='urlid',
+            field=djangoldp.fields.LDPUrlField(blank=True, null=True, unique=True),
+        ),
+    ]
diff --git a/djangoldp/models.py b/djangoldp/models.py
index 73c1c17d9a7f9d23061cf51ae222e1d9006b3ab0..c9f545dca94268162909bd98c87a30370f805f95 100644
--- a/djangoldp/models.py
+++ b/djangoldp/models.py
@@ -1,10 +1,14 @@
+from django.conf import settings
 from django.contrib.auth.models import User
 from django.db import models
 from django.db.models.base import ModelBase
+from django.db.models.signals import pre_save, post_save
+from django.dispatch import receiver
 from django.urls import get_resolver
-from django.utils.datastructures import MultiValueDict, MultiValueDictKeyError
+from django.utils.datastructures import MultiValueDictKeyError
 from django.utils.decorators import classonlymethod
 
+from djangoldp.fields import LDPUrlField
 from djangoldp.permissions import LDPPermissions
 
 User._meta.rdf_type = "foaf:user"
@@ -12,6 +16,10 @@ User._meta.owner_field = "id"
 
 
 class Model(models.Model):
+    urlid = LDPUrlField(blank=True, null=True, unique=True)
+
+    def __init__(self, *args, **kwargs):
+        super(Model, self).__init__(*args, **kwargs)
 
     @classmethod
     def get_view_set(cls):
@@ -30,7 +38,10 @@ class Model(models.Model):
         return cls.__clean_path(path)
 
     def get_absolute_url(self):
-        return Model.resource_id(self)
+        if self.urlid is  None or self.urlid != '':
+            return '{}{}'.format(settings.BASE_URL, Model.resource_id(self))
+        else:
+            return self.urlid
 
     def get_container_id(self):
         return Model.container_id(self)
@@ -54,7 +65,7 @@ class Model(models.Model):
         else:
             object_name = instance_or_model._meta.object_name.lower()
         view_name = '{}-detail'.format(object_name)
-        try :
+        try:
             slug_field = '/{}'.format(get_resolver().reverse_dict[view_name][0][0][1][0])
         except MultiValueDictKeyError:
             slug_field = Model.get_meta(instance_or_model, 'lookup_field', 'pk')
@@ -133,20 +144,34 @@ class Model(models.Model):
             permissions = permission_class().filter_user_perms(user_or_group, obj_or_model, permissions)
         return [{'mode': {'@type': name.split('_')[0]}} for name in permissions]
 
+    @classmethod
+    def is_external(cls, value):
+        try:
+            return value.urlid is not None and not value.urlid.startswith(settings.SITE_URL)
+        except:
+            return False
+
 
 class LDPSource(Model):
-    id = models.URLField(primary_key=True)
     federation = models.CharField(max_length=255)
 
     class Meta:
         rdf_type = 'ldp:Container'
         ordering = ('federation',)
         container_path = 'sources'
-        lookup_field = 'id'
+        lookup_field = 'federation'
         permissions = (
             ('view_source', 'acl:Read'),
             ('control_source', 'acl:Control'),
         )
 
     def __str__(self):
-        return "{}: {}".format(self.federation, self.id)
+        return "{}: {}".format(self.federation, self.urlid)
+
+
+@receiver([post_save])
+def auto_urlid(sender, instance, **kwargs):
+    if isinstance(instance, Model) and (instance.urlid is None or instance.urlid == ''):
+        instance.urlid = instance.get_absolute_url()
+        instance.save()
+
diff --git a/djangoldp/serializers.py b/djangoldp/serializers.py
index 5560793a8bfd23b3d0a474ffc32a7811f9af1759..1af1ca6fc68eeb7d9b0dd1bc8cb5d8b587cfd291 100644
--- a/djangoldp/serializers.py
+++ b/djangoldp/serializers.py
@@ -3,6 +3,7 @@ from typing import Any
 from urllib import parse
 
 from django.conf import settings
+from django.contrib.auth import get_user_model
 from django.core.exceptions import ImproperlyConfigured
 from django.core.exceptions import ValidationError as DjangoValidationError
 from django.core.urlresolvers import get_resolver, resolve, get_script_prefix, Resolver404
@@ -184,7 +185,10 @@ class JsonLdField(HyperlinkedRelatedField):
 class JsonLdRelatedField(JsonLdField):
     def to_representation(self, value):
         try:
-            return {'@id': super().to_representation(value)}
+            if Model.is_external(value):
+                return {'@id': value.urlid }
+            else:
+                return {'@id': super().to_representation(value)}
         except ImproperlyConfigured:
             return value.pk
 
@@ -223,10 +227,20 @@ class JsonLdIdentityField(JsonLdField):
 
     def to_representation(self, value: Any) -> Any:
         try:
-            return Hyperlink(value.webid(), value)
+            if isinstance(value, str):
+                return Hyperlink(value, value)
+            else:
+                return Hyperlink(value.webid(), value)
         except AttributeError:
             return super().to_representation(value)
 
+    def get_attribute(self, instance):
+        if Model.is_external(instance):
+            return instance.urlid
+        else:
+            return super().get_attribute(instance)
+
+
 
 class LDPSerializer(HyperlinkedModelSerializer):
     url_field_name = "@id"
@@ -254,10 +268,10 @@ class LDPSerializer(HyperlinkedModelSerializer):
         for field in data:
             if isinstance(data[field], dict) and '@id' in data[field]:
                 data[field]['@id'] = data[field]['@id'].format(Model.container_id(obj), str(getattr(obj, slug_field)))
-        if not ('@id' in data or 'id' in data):
+        if 'urlid' in data and data['urlid'] is not None:
+            data['@id'] = data.pop('urlid')['@id']
+        if not '@id' in data:
             data['@id'] = '{}{}'.format(settings.SITE_URL, Model.resource(obj))
-        if 'id' in data:
-            data['@id'] = data.pop('id')
         rdf_type = Model.get_meta(obj, 'rdf_type', None)
         rdf_context = Model.get_meta(obj, 'rdf_context', None)
         if rdf_type is not None:
@@ -326,6 +340,8 @@ class LDPSerializer(HyperlinkedModelSerializer):
                 super().__init__(**kwargs)
 
             def get_value(self, dictionary):
+                if self.field_name == 'urlid':
+                    self.field_name = '@id'
                 try:
                     object_list = dictionary["@graph"]
                     if self.parent.instance is None:
@@ -341,6 +357,9 @@ class LDPSerializer(HyperlinkedModelSerializer):
                 except KeyError:
                     value = super().get_value(dictionary)
 
+                if self.field_name == '@id' and value == './':
+                    self.field_name = 'urlid'
+                    return None
                 return self.manage_empty(value)
 
             def manage_empty(self, value):
@@ -371,6 +390,8 @@ class LDPSerializer(HyperlinkedModelSerializer):
                     fields = '__all__'
 
             def to_internal_value(self, data):
+                if self.url_field_name in data and not 'urlid' in data and data[self.url_field_name].startswith('http'):
+                    data['urlid'] = data[self.url_field_name]
                 if data is '':
                     return ''
                 if self.url_field_name in data:
@@ -384,6 +405,7 @@ class LDPSerializer(HyperlinkedModelSerializer):
 
                     ret = OrderedDict()
                     errors = OrderedDict()
+
                     fields = list(filter(lambda x: x.field_name in data, self._writable_fields))
 
                     for field in fields:
@@ -419,7 +441,8 @@ class LDPSerializer(HyperlinkedModelSerializer):
                         slug_field = Model.slug_field(self.__class__.Meta.model)
                         ret[slug_field] = match.kwargs[slug_field]
                     except Resolver404:
-                        pass
+                        if 'urlid' in data:
+                            ret['urlid'] = data['urlid']
 
                     return ret
                 else:
@@ -438,6 +461,15 @@ class LDPSerializer(HyperlinkedModelSerializer):
             serializer.id = '{}{}/'.format(serializer.id, kwargs['context']['view'].nested_field)
         return serializer
 
+    def to_internal_value(self, data):
+        user_case = self.Meta.model is get_user_model() and '@id' in data and not data['@id'].startswith(settings.BASE_URL)
+        if user_case:
+            data['username'] = 'external'
+        ret = super().to_internal_value(data)
+        if user_case:
+            ret['username'] = data['@id']
+        return ret
+
     def get_value(self, dictionary):
         try:
             object_list = dictionary["@graph"]
@@ -511,6 +543,8 @@ class LDPSerializer(HyperlinkedModelSerializer):
                     field_name in validated_data) and not field_name is None:
                 many_to_many.append((field_name, validated_data.pop(field_name)))
         validated_data = self.remove_empty_value(validated_data)
+        if model is get_user_model() and 'urlid' in validated_data and not 'username' in validated_data:
+            validated_data['username'] = validated_data.pop('urlid')
         instance = model.objects.create(**validated_data)
 
         for field_name, value in many_to_many:
@@ -540,9 +574,9 @@ class LDPSerializer(HyperlinkedModelSerializer):
             else:
                 setattr(instance, attr, value)
 
-        instance.save()
 
         self.save_or_update_nested_list(instance, nested_fields)
+        instance.save()
 
         return instance
 
diff --git a/djangoldp/tests/models.py b/djangoldp/tests/models.py
index fe6de31f645f445108295ef3f325f6ea05ea8768..f70c1319ef00025805a32f627de288935be3896e 100644
--- a/djangoldp/tests/models.py
+++ b/djangoldp/tests/models.py
@@ -1,6 +1,8 @@
+import validators
 from django.conf import settings
 from django.contrib.auth import get_user_model
 from django.db import models
+from django.urls import reverse_lazy
 from django.utils.datetime_safe import date
 
 from djangoldp.models import Model
@@ -24,7 +26,7 @@ class Skill(Model):
 
 
 class JobOffer(Model):
-    title = models.CharField(max_length=255, blank=True, null=True)
+    title = models.CharField(max_length=255, null=True)
     skills = models.ManyToManyField(Skill, blank=True)
     slug = models.SlugField(blank=True, null=True, unique=True)
     date = models.DateTimeField(auto_now_add=True, blank=True)
@@ -40,7 +42,7 @@ class JobOffer(Model):
         authenticated_perms = ['inherit', 'change', 'add']
         owner_perms = ['inherit', 'delete', 'control']
         nested_fields = ["skills"]
-        serializer_fields = ["@id", "title", "skills", "recent_skills", "resources", "slug", "some_skill"]
+        serializer_fields = ["@id", "title", "skills", "recent_skills", "resources", "slug", "some_skill", "urlid"]
         container_path = "job-offers/"
         lookup_field = 'slug'
 
@@ -58,6 +60,7 @@ class Conversation(models.Model):
 
 class Resource(Model):
     joboffers = models.ManyToManyField(JobOffer, blank=True, related_name='resources')
+    description = models.CharField(max_length=255)
 
     class Meta:
         anonymous_perms = ['view', 'add', 'delete', 'add', 'change', 'control']
@@ -157,5 +160,28 @@ class Post(Model):
         owner_perms = ['inherit']
 
 
+class Circle(Model):
+    description = models.CharField(max_length=255)
+    team = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True)
+
+    class Meta:
+        nested_fields = ["team"]
+        anonymous_perms = ['view', 'add', 'delete', 'add', 'change', 'control']
+        authenticated_perms = ["inherit"]
+        rdf_type = 'hd:circle'
+        depth = 1
+
+
+def webid(self):
+    # hack : We user webid as username for external user (since it's an uniq identifier too)
+    if validators.url(self.username):
+        webid = self.username
+    else:
+        webid = '{0}{1}'.format(settings.BASE_URL, reverse_lazy('user-detail', kwargs={'pk': self.pk}))
+    return webid
+
+
 get_user_model()._meta.serializer_fields = ['@id', 'username', 'first_name', 'last_name', 'email', 'userprofile',
-                                            'conversation_set', ]
+                                            'conversation_set', 'circle_set']
+get_user_model().webid = webid
+get_user_model()._meta.anonymous_perms = ['view', 'add']
diff --git a/djangoldp/tests/runner.py b/djangoldp/tests/runner.py
index 9b83a4fc2f7eaa059ada26a6689aceb6e95d284e..f0fb6b439daeedd5d4d86ef00f815fa6138324a8 100644
--- a/djangoldp/tests/runner.py
+++ b/djangoldp/tests/runner.py
@@ -49,7 +49,8 @@ settings.configure(DEBUG=False,
                                    'djangoldp.tests',
                                    ),
                    SITE_URL='http://happy-dev.fr',
-                   REST_FRAMEWORK={
+                   BASE_URL='http://happy-dev.fr',
+                   REST_FRAMEWORK = {
                        'DEFAULT_PAGINATION_CLASS': 'djangoldp.pagination.LDPPagination',
                        'PAGE_SIZE': 5
                    },
@@ -71,7 +72,7 @@ failures = test_runner.run_tests([
     'djangoldp.tests.tests_delete',
     'djangoldp.tests.tests_sources',
     'djangoldp.tests.tests_pagination',
-    'djangoldp.tests.tests_temp'
+    # 'djangoldp.tests.tests_temp'
 
 ])
 if failures:
diff --git a/djangoldp/tests/tests_get.py b/djangoldp/tests/tests_get.py
index fb2c0b9925d0868fa4df0cd4b199be9a8ba58a38..e69f119f23cbef0094600ba50b1c330f6c58f360 100644
--- a/djangoldp/tests/tests_get.py
+++ b/djangoldp/tests/tests_get.py
@@ -42,14 +42,17 @@ class TestGET(APITestCase):
     def test_get_filtered_fields(self):
         skill = Skill.objects.create(title="Java", obligatoire="ok", slug="1")
         skill2 = Skill.objects.create(title="Java", obligatoire="ok", slug="2")
+        skill3 = Skill.objects.create(urlid="http://external/skills/1")
         job = JobOffer.objects.create(title="job", slug="1")
         job.skills.add(skill)
         job.skills.add(skill2)
+        job.skills.add(skill3)
         job.save()
         response = self.client.get('/job-offers/{}/'.format(job.slug), content_type='application/ld+json')
         self.assertEqual(response.status_code, 200)
         self.assertIn('recent_skills', response.data)
         self.assertEqual(response.data['recent_skills']['@id'], "http://happy-dev.fr/job-offers/1/recent_skills/")
+        self.assertEqual(response.data['skills']['ldp:contains'][2]['@id'], "http://external/skills/1")
 
     def test_get_reverse_filtered_fields(self):
         skill = Skill.objects.create(title="Java", obligatoire="ok", slug="1")
diff --git a/djangoldp/tests/tests_ldp_model.py b/djangoldp/tests/tests_ldp_model.py
index 14217f94a0dc1985f0ee4ea8d71f64a43caa51e7..bc316ed50914dea6fb6fc99847392fa9d09e2562 100644
--- a/djangoldp/tests/tests_ldp_model.py
+++ b/djangoldp/tests/tests_ldp_model.py
@@ -16,7 +16,7 @@ class LDPModelTest(TestCase):
     def test_class_inheriting_ldp_model(self):
         dummy = LDPDummy.objects.create(some="text")
         self.assertEquals("/ldpdummys/", dummy.get_container_id())
-        self.assertEquals("/ldpdummys/{}/".format(dummy.pk), dummy.get_absolute_url())
+        self.assertEquals("http://happy-dev.fr/ldpdummys/{}/".format(dummy.pk), dummy.get_absolute_url())
         self.assertEquals("/ldpdummys/", Model.container_id(dummy))
         self.assertEquals("/ldpdummys/{}/".format(dummy.pk), Model.resource_id(dummy))
 
@@ -33,6 +33,6 @@ class LDPModelTest(TestCase):
         from django.urls import get_resolver
         dummy = LDPDummy.objects.create(some="text")
         view_name = '{}-list'.format(dummy._meta.object_name.lower())
-        path = '/{}{}/'.format(get_resolver().reverse_dict[view_name][0][0][0], dummy.pk)
+        path = 'http://happy-dev.fr/{}{}/'.format(get_resolver().reverse_dict[view_name][0][0][0], dummy.pk)
 
         self.assertEquals(path, dummy.get_absolute_url())
diff --git a/djangoldp/tests/tests_save.py b/djangoldp/tests/tests_save.py
index 0674d12a6e7fe5bc85f5f332f7d7dddaffe8e30d..0c97d5c356520adfa266c1b75a6c5426b95ebbd1 100644
--- a/djangoldp/tests/tests_save.py
+++ b/djangoldp/tests/tests_save.py
@@ -1,13 +1,25 @@
+from django.contrib.auth import get_user_model
 from django.test import TestCase
+from rest_framework.test import APIRequestFactory, APIClient
 from rest_framework.utils import json
 
 from djangoldp.models import Model
 from djangoldp.serializers import LDPSerializer
-from djangoldp.tests.models import Skill, JobOffer, Invoice, LDPDummy, Resource
+from djangoldp.tests.models import Skill, JobOffer, Invoice, LDPDummy, Resource, Post, Circle
 
 
 class Save(TestCase):
 
+    def setUp(self):
+        self.factory = APIRequestFactory()
+        self.client = APIClient()
+        self.user = get_user_model().objects.create_user(username='john', email='jlennon@beatles.com',
+                                                         password='glass onion')
+        self.client.force_authenticate(self.user)
+
+    def tearDown(self):
+        pass
+
     def test_save_m2m_graph_with_many_nested(self):
         invoice = {
             "@graph": [
@@ -88,8 +100,8 @@ class Save(TestCase):
         self.assertIs(result.skills.count(), 0)
 
     def test_save_m2m_graph_with_nested(self):
-        skill1 = Skill.objects.create(title="skill1", obligatoire="obligatoire")
-        skill2 = Skill.objects.create(title="skill2", obligatoire="obligatoire")
+        skill1 = Skill.objects.create(title="skill1", obligatoire="obligatoire", slug="a")
+        skill2 = Skill.objects.create(title="skill2", obligatoire="obligatoire", slug="b")
 
         job = {"@graph": [
             {"title": "job test",
@@ -111,9 +123,9 @@ class Save(TestCase):
         self.assertEquals(result.skills.all()[0].title, "skill3 NEW")  # creation on the fly
 
     def test_save_without_nested_fields(self):
-        skill1 = Skill.objects.create(title="skill1", obligatoire="obligatoire")
-        skill2 = Skill.objects.create(title="skill2", obligatoire="obligatoire")
-        job = {"title": "job test"}
+        skill1 = Skill.objects.create(title="skill1", obligatoire="obligatoire", slug="a")
+        skill2 = Skill.objects.create(title="skill2", obligatoire="obligatoire", slug="b")
+        job = {"title": "job test", "slug": "c"}
 
         meta_args = {'model': JobOffer, 'depth': 2, 'fields': ("@id", "title", "skills")}
 
@@ -265,9 +277,24 @@ class Save(TestCase):
                                     data=json.dumps(body),
                                     content_type='application/ld+json')
         self.assertEqual(response.status_code, 201)
-        self.assertEqual(response.data['resources']['ldp:contains'][0]['@id'], "http://testserver/resources/{}/".format(resource.pk))
+        self.assertEqual(response.data['resources']['ldp:contains'][0]['@id'],
+                         "http://testserver/resources/{}/".format(resource.pk))
         self.assertEqual(response.data['title'], "new job")
 
+    def test_nested_container_federated(self):
+        resource = Resource.objects.create()
+        body = {
+            'http://happy-dev.fr/owl/#@id': "http://external.job/job/1",
+        }
+
+        response = self.client.post('/resources/{}/joboffers/'.format(resource.pk),
+                                    data=json.dumps(body),
+                                    content_type='application/ld+json')
+        self.assertEqual(response.status_code, 201)
+        self.assertEqual(response.data['resources']['ldp:contains'][0]['@id'],
+                         "http://testserver/resources/{}/".format(resource.pk))
+        self.assertEqual(response.data['@id'], "http://external.job/job/1")
+
     def test_embedded_context_2(self):
         body = {
             '@id': "./",
@@ -281,3 +308,33 @@ class Save(TestCase):
         response = self.client.post('/posts/', data=json.dumps(body),
                                     content_type='application/ld+json')
         self.assertEqual(response.status_code, 201)
+
+    def test_auto_id(self):
+        body = {
+            '@id': "./",
+            'content': "post update",
+            'peer_user': "",
+            '@context': {
+                "@vocab": "http://happy-dev.fr/owl/#",
+            }
+        }
+
+        response = self.client.post('/posts/', data=json.dumps(body),
+                                    content_type='application/ld+json')
+        self.assertEqual(response.status_code, 201)
+        saved_post = Post.objects.get(pk=1)
+        self.assertEqual(saved_post.urlid, "http://happy-dev.fr/posts/1/")
+
+    def test_nested_container_user_federated(self):
+        circle = Circle.objects.create()
+        body = {
+            'http://happy-dev.fr/owl/#@id': "http://external.user/user/1/",
+        }
+
+        response = self.client.post('/circles/{}/team/'.format(circle.pk),
+                                    data=json.dumps(body),
+                                    content_type='application/ld+json')
+        self.assertEqual(response.status_code, 201)
+        self.assertEqual(response.data['circle_set']['ldp:contains'][0]['@id'],
+                         "http://testserver/circles/{}/".format(circle.pk))
+        self.assertEqual(response.data['@id'], "http://external.user/user/1/")
diff --git a/djangoldp/tests/tests_sources.py b/djangoldp/tests/tests_sources.py
index a68327f56c190503210d48870be5a95f1bb3c786..e43f3e9457cf9d828da01b85b6531b8e199aeca9 100644
--- a/djangoldp/tests/tests_sources.py
+++ b/djangoldp/tests/tests_sources.py
@@ -13,6 +13,6 @@ class TestSource(APITestCase):
         pass
 
     def test_get_resource(self):
-        source = LDPSource.objects.create(federation="source_name", id="http://bar.foo/")
+        source = LDPSource.objects.create(federation="source_name", urlid="http://bar.foo/")
         response = self.client.get('/sources/{}/'.format(source.federation), content_type='application/ld+json')
         self.assertEqual(response.status_code, 200)
diff --git a/djangoldp/tests/tests_temp.py b/djangoldp/tests/tests_temp.py
index 161703c81abc8562d445f07bd2e04fc503ee5de0..ef7792a2a2cb08b941457637778f05cb736becd5 100644
--- a/djangoldp/tests/tests_temp.py
+++ b/djangoldp/tests/tests_temp.py
@@ -1,19 +1,14 @@
-import json
-
 from django.contrib.auth.models import User
 from django.test import TestCase
 from rest_framework.test import APIRequestFactory, APIClient
 
-from djangoldp.tests.models import Resource, JobOffer, Invoice, Batch
-
-
 class TestTemp(TestCase):
 
     def setUp(self):
         self.factory = APIRequestFactory()
         self.client = APIClient()
         self.user = User.objects.create_user(username='john', email='jlennon@beatles.com', password='glass onion')
+        self.client.force_authenticate(self.user)
 
     def tearDown(self):
         pass
-
diff --git a/djangoldp/tests/tests_update.py b/djangoldp/tests/tests_update.py
index 431db723282d3467746d77162ca92c2764ac843b..a9a7dca25d4582ecf4d16e21761c74c6f99c3755 100644
--- a/djangoldp/tests/tests_update.py
+++ b/djangoldp/tests/tests_update.py
@@ -4,7 +4,7 @@ from rest_framework.test import APIRequestFactory, APIClient
 from rest_framework.utils import json
 
 from djangoldp.serializers import LDPSerializer
-from djangoldp.tests.models import Post, UserProfile, Resource
+from djangoldp.tests.models import Post, UserProfile, Resource, Circle
 from djangoldp.tests.models import Skill, JobOffer, Conversation, Message
 
 
@@ -264,7 +264,7 @@ class Update(TestCase):
     def test_put_resource(self):
         post = Post.objects.create(content="content")
         body = [{
-            '@id': '/posts/{}/'.format(post.pk),
+            '@id': 'http://testserver.com/posts/{}/'.format(post.pk),
             'http://happy-dev.fr/owl/#content': "post content"}]
         response = self.client.put('/posts/{}/'.format(post.pk), data=json.dumps(body),
                                    content_type='application/ld+json')
@@ -401,7 +401,7 @@ class Update(TestCase):
         job = JobOffer.objects.create(title="first title", slug="job")
         body = {
             'http://happy-dev.fr/owl/#joboffers': {
-                '@id': 'http://testserver/job-offers/{}/'.format(job.slug),
+                '@id': 'http://testserver.com/job-offers/{}/'.format(job.slug),
             }
         }
 
@@ -410,7 +410,7 @@ class Update(TestCase):
                                    content_type='application/ld+json')
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.data['joboffers']['ldp:contains'][0]['@id'],
-                         "http://testserver/job-offers/{}/".format(job.slug))
+                         "http://testserver.com/job-offers/{}/".format(job.slug))
         self.assertEqual(response.data['joboffers']['ldp:contains'][0]['title'], "first title")
 
     def test_m2m_new_link_bis(self):
@@ -419,9 +419,9 @@ class Update(TestCase):
         body = {
             'http://happy-dev.fr/owl/#joboffers':
                 {
-                    '@id': "http://testserver/resources/{}/joboffers/".format(resource.pk),
+                    '@id': "http://testserver.com/resources/{}/joboffers/".format(resource.pk),
                     'ldp:contains': [
-                        {'@id': 'http://testserver/job-offers/{}/'.format(job.slug),
+                        {'@id': 'http://testserver.com/job-offers/{}/'.format(job.slug),
                          'http://happy-dev.fr/owl/#title': "new job",
                          },
                     ]
@@ -433,7 +433,7 @@ class Update(TestCase):
                                    content_type='application/ld+json')
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.data['joboffers']['ldp:contains'][0]['@id'],
-                         "http://testserver/job-offers/{}/".format(job.slug))
+                         "http://testserver.com/job-offers/{}/".format(job.slug))
         self.assertEqual(response.data['joboffers']['ldp:contains'][0]['title'], "new job")
 
     def test_m2m_new_link_embedded(self):
@@ -450,7 +450,7 @@ class Update(TestCase):
                                    content_type='application/ld+json')
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.data['joboffers']['ldp:contains'][0]['@id'],
-                         "http://testserver/job-offers/aaa/")
+                         "http://happy-dev.fr/job-offers/aaa/")
         self.assertEqual(response.data['joboffers']['ldp:contains'][0]['title'], "new job")
 
     def test_m2m_existing_link(self):
@@ -463,7 +463,7 @@ class Update(TestCase):
                 # '@id': "http://testserver/resources/{}/joboffers/".format(resource.pk),
                 'ldp:contains': [
                     {
-                        '@id': 'http://testserver/job-offers/{}/'.format(job.slug),
+                        '@id': 'http://testserver.com/job-offers/{}/'.format(job.slug),
                         'http://happy-dev.fr/owl/#title': "new job",
                     }
                 ]
@@ -475,5 +475,36 @@ class Update(TestCase):
                                    content_type='application/ld+json')
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.data['joboffers']['ldp:contains'][0]['@id'],
-                         "http://testserver/job-offers/{}/".format(job.slug))
+                         "http://testserver.com/job-offers/{}/".format(job.slug))
         self.assertEqual(response.data['joboffers']['ldp:contains'][0]['title'], "new job")
+
+    def test_m2m_new_link_federated(self):
+        resource = Resource.objects.create()
+        body = {
+            'http://happy-dev.fr/owl/#joboffers': {
+                'http://happy-dev.fr/owl/#@id': 'http://external.job/job/1',
+            }
+        }
+
+        response = self.client.put('/resources/{}/'.format(resource.pk),
+                                   data=json.dumps(body),
+                                   content_type='application/ld+json')
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.data['joboffers']['ldp:contains'][0]['@id'],
+                         "http://external.job/job/1")
+
+    def test_m2m_user_link_federated(self):
+        circle = Circle.objects.create(description="cicle name")
+        body = {
+            'http://happy-dev.fr/owl/#description': 'circle name',
+            'http://happy-dev.fr/owl/#team': {
+                'http://happy-dev.fr/owl/#@id': 'http://external.user/user/1',
+            }
+        }
+
+        response = self.client.put('/circles/{}/'.format(circle.pk),
+                                   data=json.dumps(body),
+                                   content_type='application/ld+json')
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.data['team']['ldp:contains'][0]['@id'],
+                         "http://external.user/user/1")
diff --git a/djangoldp/urls.py b/djangoldp/urls.py
index f92a2ab43590fc8c27da7ae3b5e387e36cad273e..a83b98eb262e80c0dd805ce4bc186d02f4f74fbf 100644
--- a/djangoldp/urls.py
+++ b/djangoldp/urls.py
@@ -17,7 +17,7 @@ def __clean_path(path):
 
 
 urlpatterns = [
-    url(r'^sources/(?P<federation>\w+)/', LDPSourceViewSet.urls(model=LDPSource, fields=['federation', 'id'],
+    url(r'^sources/(?P<federation>\w+)/', LDPSourceViewSet.urls(model=LDPSource, fields=['federation', 'urlid'],
                                                                 permission_classes=[LDPPermissions], )),
     url(r'^\.well-known/webfinger/?$', WebFingerView.as_view()),
 ]
diff --git a/setup.cfg b/setup.cfg
index ab33010f208f6f2b3903821d028fe6e87151557a..46e3b9622fcb881c7063e44d1d3dceca75f3a614 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -20,6 +20,7 @@ install_requires =
 
 [options.extras_require]
 dev =
+    validators
     factory_boy>=2.11.0
 
 [semantic_release]