Skip to content

Instantly share code, notes, and snippets.

@jproby
Created February 11, 2014 21:21

Revisions

  1. jproby created this gist Feb 11, 2014.
    99 changes: 99 additions & 0 deletions apiauth.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,99 @@
    # API authentication


    from social.apps.django_app.utils import strategy
    from rest_framework.authtoken.models import Token
    from rest_framework.views import APIView
    from rest_framework import parsers
    from rest_framework import renderers
    from rest_framework.authentication import get_authorization_header
    from rest_framework.response import Response
    from rest_framework import status
    from rest_framework.authtoken.serializers import AuthTokenSerializer


    @strategy()
    def register_by_access_token(request, backend):
    backend = request.strategy.backend
    # Split by spaces and get the array
    auth = get_authorization_header(request).split()

    if not auth or auth[0].lower() != b'token':
    msg = 'No token header provided.'
    return msg

    if len(auth) == 1:
    msg = 'Invalid token header. No credentials provided.'
    return msg

    access_token = auth[1]

    user = backend.do_auth(access_token)

    return user


    # Pour une vraie integration au rest framework
    class ObtainAuthToken(APIView):
    throttle_classes = ()
    permission_classes = ()
    parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,)
    renderer_classes = (renderers.JSONRenderer,)
    serializer_class = AuthTokenSerializer
    model = Token

    # Accepte un backend en parametre : 'auth' pour un login / pass classique
    def post(self, request, backend):
    serializer = self.serializer_class(data=request.DATA)

    if backend == 'auth':
    if serializer.is_valid():
    token, created = Token.objects.get_or_create(user=serializer.object['user'])
    return Response({'token': token.key})
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    else:
    user = register_by_access_token(request, backend)

    if user and user.is_active:
    token, created = Token.objects.get_or_create(user=user)
    return Response({'id': user.id, 'name': user.username, 'firstname': user.first_name, 'userRole': 'user', 'token': token.key})


    class ObtainUser(APIView):
    throttle_classes = ()
    permission_classes = ()
    parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,)
    renderer_classes = (renderers.JSONRenderer,)
    serializer_class = AuthTokenSerializer
    model = Token

    # Renvoi le user si le token est valide
    def get(self, request):
    serializer = self.serializer_class(data=request.DATA)
    if request.META.get('HTTP_AUTHORIZATION'):

    auth = request.META.get('HTTP_AUTHORIZATION').split()

    if not auth or auth[0].lower() != b'token' or len(auth) != 2:
    msg = 'Invalid token header. No credentials provided.'
    return Response(msg, status=status.HTTP_401_UNAUTHORIZED)

    token = Token.objects.get(key=auth[1])
    if token and token.user.is_active:
    return Response({'id': token.user_id, 'name': token.user.username, 'firstname': token.user.first_name, 'userRole': 'user', 'token': token.key})
    else:
    return Response(serializer.errors, status=status.HTTP_401_UNAUTHORIZED)


    class ObtainLogout(APIView):
    throttle_classes = ()
    permission_classes = ()
    parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,)
    renderer_classes = (renderers.JSONRenderer,)
    serializer_class = AuthTokenSerializer
    model = Token

    # Logout le user
    def get(self, request):
    return Response({'User': ''})
    87 changes: 87 additions & 0 deletions app.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,87 @@
    angular.module('myApp', [
    // login service
    'loginService',

    // components
    'ngAnimate',
    'restangular'
    ])
    .config(function ($urlRouterProvider, RestangularProvider) {
    RestangularProvider.setBaseUrl('http://127.0.0.1:8000/api');
    RestangularProvider.setRequestSuffix('/');
    $urlRouterProvider.otherwise('/discover');
    })
    .run(function ($rootScope) {
    /**
    * $rootScope.doingResolve is a flag useful to display a spinner on changing states.
    * Some states may require remote data so it will take awhile to load.
    */

    var resolveDone = function () { $rootScope.doingResolve = false; };

    $rootScope.doingResolve = false;

    $rootScope.$on('$stateChangeStart', function () {
    $rootScope.doingResolve = true;
    });

    $rootScope.$on('$stateChangeSuccess', resolveDone);
    $rootScope.$on('$stateChangeError', resolveDone);
    $rootScope.$on('$permissionError', resolveDone);

    })

    .controller('AppCtrl', function ($scope, $state, $stateParams, loginService, $http) {
    // Expose $state and $stateParams to the <body> tag
    $scope.$state = $state;
    $scope.$stateParams = $stateParams;


    // loginService exposed and a new Object containing login user/pwd
    $scope.ls = loginService;
    $scope.login = {
    working: false
    };

    $scope.loginBK = function (backend) {

    if (backend == 'facebook') {
    OAuth.popup('facebook', function(error, success) {
    if (error) {

    }
    else {
    var token = "Token " + success.access_token

    var loginPromise = $http({method:'POST', url: 'http://127.0.0.1:8000/api-token/login/' + backend + '/', headers: {'Authorization': token}});
    $scope.login.working = true;

    loginService.loginUser(loginPromise);
    loginPromise.success(function () {
    $scope.login = { working: false };
    });
    loginPromise.finally(function () {
    $scope.login.working = false;
    });

    }
    });
    }

    };

    $scope.logoutMe = function () {
    loginService.logoutUser($http.get('http://127.0.0.1:8000/api-token/logout'));
    };

    // Changement du dynamique du titre
    $scope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams){
    if ( angular.isDefined( toState.data.pageTitle ) ) {
    $scope.pageTitle = toState.data.pageTitle + ' | Welcome' ;
    }
    });

    // initilization de oauth.io
    OAuth.initialize('your oauth.io key');

    });
    345 changes: 345 additions & 0 deletions base.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,345 @@
    # -*- coding: UTF-8 -*-
    """Common settings and globals."""

    from os.path import abspath, basename, dirname, join, normpath
    from sys import path
    from django.utils.translation import gettext_lazy as _


    ########## PATH CONFIGURATION
    # Absolute filesystem path to the Django project directory:
    DJANGO_ROOT = dirname(dirname(abspath(__file__)))

    # Absolute filesystem path to the top-level project folder:
    SITE_ROOT = dirname(DJANGO_ROOT)

    # Site name:
    SITE_NAME = basename(DJANGO_ROOT)

    # Add our project to our pythonpath, this way we don't need to type our project
    # name in our dotted import paths:
    path.append(DJANGO_ROOT)
    ########## END PATH CONFIGURATION


    ########## DEBUG CONFIGURATION
    # See: https://docs.djangoproject.com/en/dev/ref/settings/#debug
    DEBUG = False

    # See: https://docs.djangoproject.com/en/dev/ref/settings/#template-debug
    TEMPLATE_DEBUG = DEBUG
    ########## END DEBUG CONFIGURATION


    ########## MANAGER CONFIGURATION
    # See: https://docs.djangoproject.com/en/dev/ref/settings/#admins
    ADMINS = (
    ('Your Name', '[email protected]'),
    )

    # See: https://docs.djangoproject.com/en/dev/ref/settings/#managers
    MANAGERS = ADMINS
    ########## END MANAGER CONFIGURATION


    ########## DATABASE CONFIGURATION
    # See: https://docs.djangoproject.com/en/dev/ref/settings/#databases
    DATABASES = {
    'default': {
    'ENGINE': 'django.db.backends.',
    'NAME': '',
    'USER': '',
    'PASSWORD': '',
    'HOST': '',
    'PORT': '',
    }
    }
    ########## END DATABASE CONFIGURATION


    ########## GENERAL CONFIGURATION
    # See: https://docs.djangoproject.com/en/dev/ref/settings/#time-zone
    TIME_ZONE = 'Europe/Paris'

    # See: https://docs.djangoproject.com/en/dev/ref/settings/#language-code
    LANGUAGE_CODE = 'fr'

    # See: https://docs.djangoproject.com/en/dev/ref/settings/#site-id
    SITE_ID = 1

    # See: https://docs.djangoproject.com/en/dev/ref/settings/#use-i18n
    USE_I18N = True

    # See: https://docs.djangoproject.com/en/dev/ref/settings/#use-l10n
    USE_L10N = True

    # See: https://docs.djangoproject.com/en/dev/ref/settings/#use-tz
    USE_TZ = True

    LANGUAGES = (
    ('fr', _('Français')),
    ('en', _('Anglais')),
    )

    DEFAULT_LANGUAGE = 1

    ########## END GENERAL CONFIGURATION


    ########## MEDIA CONFIGURATION
    # See: https://docs.djangoproject.com/en/dev/ref/settings/#media-root
    MEDIA_ROOT = normpath(join(SITE_ROOT, 'media'))

    # See: https://docs.djangoproject.com/en/dev/ref/settings/#media-url
    MEDIA_URL = '/media/'

    ########## END MEDIA CONFIGURATION


    ########## STATIC FILE CONFIGURATION
    # See: https://docs.djangoproject.com/en/dev/ref/settings/#static-root
    STATIC_ROOT = normpath(join(SITE_ROOT, 'assets'))

    # See: https://docs.djangoproject.com/en/dev/ref/settings/#static-url
    STATIC_URL = '/static/'

    # See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#std:setting-STATICFILES_DIRS
    STATICFILES_DIRS = (
    normpath(join(SITE_ROOT, 'static')),
    )

    # See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#staticfiles-finders
    STATICFILES_FINDERS = (
    'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
    )
    ########## END STATIC FILE CONFIGURATION


    ########## SECRET CONFIGURATION
    # See: https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
    # Note: This key only used for development and testing.
    SECRET_KEY = r"secret"
    ########## END SECRET CONFIGURATION


    ########## FIXTURE CONFIGURATION
    # See: https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-FIXTURE_DIRS
    FIXTURE_DIRS = (
    normpath(join(SITE_ROOT, 'fixtures')),
    )
    ########## END FIXTURE CONFIGURATION


    ########## TEMPLATE CONFIGURATION
    # See: https://docs.djangoproject.com/en/dev/ref/settings/#template-context-processors
    TEMPLATE_CONTEXT_PROCESSORS = (
    'django.contrib.auth.context_processors.auth',
    'django.core.context_processors.debug',
    'django.core.context_processors.i18n',
    'django.core.context_processors.media',
    'django.core.context_processors.static',
    'django.core.context_processors.tz',
    'django.contrib.messages.context_processors.messages',
    'django.core.context_processors.request',

    'social.apps.django_app.context_processors.backends',
    'social.apps.django_app.context_processors.login_redirect',

    'account.context_processors.account',

    )

    # See: https://docs.djangoproject.com/en/dev/ref/settings/#template-loaders
    TEMPLATE_LOADERS = (
    'django.template.loaders.filesystem.Loader',
    'django.template.loaders.app_directories.Loader',
    )

    # See: https://docs.djangoproject.com/en/dev/ref/settings/#template-dirs
    TEMPLATE_DIRS = (
    normpath(join(SITE_ROOT, 'templates')),
    )
    ########## END TEMPLATE CONFIGURATION


    ########## MIDDLEWARE CONFIGURATION
    # See: https://docs.djangoproject.com/en/dev/ref/settings/#middleware-classes
    MIDDLEWARE_CLASSES = (
    # Default Django middleware.
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.locale.LocaleMiddleware',
    'corsheaders.middleware.CorsMiddleware',

    'social.apps.django_app.middleware.SocialAuthExceptionMiddleware',
    "account.middleware.TimezoneMiddleware"



    )
    ########## END MIDDLEWARE CONFIGURATION


    ########## URL CONFIGURATION
    # See: https://docs.djangoproject.com/en/dev/ref/settings/#root-urlconf
    ROOT_URLCONF = '%s.urls' % SITE_NAME
    ########## END URL CONFIGURATION


    ########## APP CONFIGURATION

    INSTALLED_APPS = (

    # Default Django apps:
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.comments',

    # Useful template tags:
    # 'django.contrib.humanize',

    # Admin panel and documentation:
    'django.contrib.admin',

    # 3rd party direct social auth
    'social.apps.django_app.default',

    #'pinax_theme_bootstrap_account',
    #'pinax_theme_bootstrap',

    # Users Account
    'account',
    'avatar',

    # rest framework et oauth for rest API
    #'provider',
    #'provider.oauth2',
    'rest_framework',
    'rest_framework.authtoken',
    'corsheaders',

    # data storage and db utils
    'south', 'storages',

    # External apps

    'qhonuskan_votes', 'taggit',


    )

    # INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS
    ########## END APP CONFIGURATION


    ########## LOGGING CONFIGURATION
    # See: https://docs.djangoproject.com/en/dev/ref/settings/#logging
    # A sample logging configuration. The only tangible logging
    # performed by this configuration is to send an email to
    # the site admins on every HTTP 500 error when DEBUG=False.
    # See http://docs.djangoproject.com/en/dev/topics/logging for
    # more details on how to customize your logging configuration.
    LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'filters': {
    'require_debug_false': {
    '()': 'django.utils.log.RequireDebugFalse'
    }
    },
    'handlers': {
    'mail_admins': {
    'level': 'ERROR',
    'filters': ['require_debug_false'],
    'class': 'django.utils.log.AdminEmailHandler'
    }
    },
    'loggers': {
    'django.request': {
    'handlers': ['mail_admins'],
    'level': 'ERROR',
    'propagate': True,
    },
    }
    }
    ########## END LOGGING CONFIGURATION



    ########## REST CONF

    REST_FRAMEWORK = {
    # Use hyperlinked styles by default.
    # Only used if the `serializer_class` attribute is not set on a view.
    'DEFAULT_MODEL_SERIALIZER_CLASS':
    'rest_framework.serializers.HyperlinkedModelSerializer',

    # Use Django's standard `django.contrib.auth` permissions,
    # or allow read-only access for unauthenticated users.
    'DEFAULT_PERMISSION_CLASSES': [
    'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
    ]
    }

    ########## END REST CONF



    ########## WSGI CONFIGURATION
    # See: https://docs.djangoproject.com/en/dev/ref/settings/#wsgi-application
    WSGI_APPLICATION = 'wsgi.application'
    ########## END WSGI CONFIGURATION

    ########## Auth

    AUTHENTICATION_BACKENDS = (
    'social.backends.google.GoogleOAuth2',
    'social.backends.twitter.TwitterOAuth',
    'social.backends.facebook.FacebookOAuth2',
    'django.contrib.auth.backends.ModelBackend'
    )

    ACCOUNT_EMAIL_UNIQUE = True
    ACCOUNT_EMAIL_CONFIRMATION_REQUIRED = True

    SOCIAL_AUTH_PIPELINE = (
    'social.pipeline.social_auth.social_details',
    'social.pipeline.social_auth.social_uid',
    'social.pipeline.social_auth.auth_allowed',
    'social.pipeline.social_auth.social_user',
    'social.pipeline.user.get_username',
    'social.pipeline.social_auth.associate_by_email',
    'social.pipeline.user.create_user',
    'social.pipeline.social_auth.associate_user',
    'social.pipeline.social_auth.load_extra_data',
    'social.pipeline.user.user_details'
    )


    CORS_ORIGIN_ALLOW_ALL = True
    #CORS_URLS_REGEX = r'^/api/.*$'
    #CORS_ALLOW_HEADERS = (
    # 'x-requested-with',
    # 'content-type',
    # 'accept',
    # 'origin',
    # 'authorization',
    # 'x-csrftoken',
    # 'x-token'
    # )



    SOCIAL_AUTH_FACEBOOK_KEY = 'your_key_here'
    SOCIAL_AUTH_FACEBOOK_SECRET = 'your_secret_here'
    SOCIAL_AUTH_FACEBOOK_SCOPE = ['email', 'user_about_me', 'user_birthday', 'user_location']

    ########## End Auth
    207 changes: 207 additions & 0 deletions login-service.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,207 @@
    angular.module('loginService', [])
    .provider('loginService', function () {
    var userToken = localStorage.getItem('userToken'),
    errorState = 'app.error',
    logoutState = 'app.discover';

    this.$get = function ($rootScope, $http, $q, $state, Restangular) {
    /**
    * Low-level, private functions.
    */

    var setHeaders = function (token) {
    if (!token) {
    delete $http.defaults.headers.common['X-Token'];
    return;
    }
    $http.defaults.headers.common['authorization'] = 'Token ' + token.toString();
    Restangular.setDefaultHeaders({'authorization': 'Token ' + token.toString()});

    };

    var setToken = function (token) {
    if (!token) {
    localStorage.removeItem('userToken');
    } else {
    localStorage.setItem('userToken', token);
    }
    setHeaders(token);
    };

    var getLoginData = function () {
    if (userToken) {
    setHeaders(userToken);
    } else {
    wrappedService.userRole = userRoles.public;
    wrappedService.isLogged = false;
    wrappedService.doneLoading = true;
    }
    };

    var managePermissions = function () {
    // Register routing function.
    $rootScope.$on('$stateChangeStart', function (event, to, toParams, from, fromParams) {

    /**
    * $stateChangeStart is a synchronous check to the accessLevels property
    * if it's not set, it will setup a pendingStateChange and will let
    * the grandfather resolve do his job.
    *
    * In short:
    * If accessLevels is still undefined, it let the user change the state.
    * Grandfather.resolve will either let the user in or reject the promise later!
    */
    if (wrappedService.userRole === null) {
    wrappedService.doneLoading = false;
    wrappedService.pendingStateChange = {
    to: to,
    toParams: toParams
    };
    return;
    }

    // if the state has undefined accessLevel, anyone can access it.
    // NOTE: if `wrappedService.userRole === undefined` means the service still doesn't know the user role,
    // we need to rely on grandfather resolve, so we let the stateChange success, for now.
    if (to.accessLevel === undefined || to.accessLevel.bitMask & wrappedService.userRole.bitMask) {
    angular.noop(); // requested state can be transitioned to.
    } else {
    event.preventDefault();
    // test this
    $state.go(errorState, { error: 'unauthorized' }, { location: false, inherit: false });
    $rootScope.$emit('$permissionError');
    }
    });

    // Gets triggered when a resolve isn't fulfilled
    // da aggiungere un caso in cui il resolve da informazioni solo ad un admin e non ad un user
    // quindi un url ad esempio /resource/admin
    // anche un url /resource/user
    // in questo modo si potrà vedere l'error redirect!
    // anche un caso in cui sono io che faccio fallire una $q cosi si vede l'errore stringa!
    $rootScope.$on('$stateChangeError', function (event, to, toParams, from, fromParams, error) {
    /**
    * This is a very clever way to implement failure redirection.
    * You can use the value of redirectMap, based on the value of the rejection
    * So you can setup DIFFERENT redirections based on different promise errors.
    */
    var errorObj, redirectObj;
    // in case the promise given to resolve function is an $http request
    // the error is a object containing the error and additional informations
    error = (typeof error === 'object') ? error.status.toString() : error;
    // in case of a random 4xx/5xx status code from server, user gets loggedout
    // otherwise it *might* forever loop (look call diagram)
    if (/^[45]\d{2}$/.test(error)) {
    wrappedService.logoutUser();
    }
    /**
    * Generic redirect handling.
    * If a state transition has been prevented and it's not one of the 2 above errors, means it's a
    * custom error in your application.
    *
    * redirectMap should be defined in the $state(s) that can generate transition errors.
    */
    if (angular.isDefined(to.redirectMap) && angular.isDefined(to.redirectMap[error])) {
    if (typeof to.redirectMap[error] === 'string') {
    return $state.go(to.redirectMap[error], { error: error }, { location: false, inherit: false });
    } else if (typeof to.redirectMap[error] === 'object') {
    redirectObj = to.redirectMap[error];
    return $state.go(redirectObj.state, { error: redirectObj.prefix + error }, { location: false, inherit: false });
    }
    }
    return $state.go(errorState, { error: error }, { location: false, inherit: false });
    });
    };

    /**
    * High level, public methods
    */

    var wrappedService = {
    loginHandler: function (user, status, headers, config) {
    /**
    * Custom logic to manually set userRole goes here
    *
    * Commented example shows an userObj coming with a 'completed'
    * property defining if the user has completed his registration process,
    * validating his/her email or not.
    *
    * EXAMPLE:
    * if (user.hasValidatedEmail) {
    * wrappedService.userRole = userRoles.registered;
    * } else {
    * wrappedService.userRole = userRoles.invalidEmail;
    * $state.go('app.nagscreen');
    * }
    */
    // setup token
    setToken(user.token);
    // update user
    angular.extend(wrappedService.user, user);
    // flag true on isLogged
    wrappedService.isLogged = true;
    // update userRole
    wrappedService.userRole = userRoles[user.userRole];
    return user;
    },

    loginUser: function (httpPromise) {
    httpPromise.success(this.loginHandler);
    },

    logoutUser: function (httpPromise) {
    /**
    * De-registers the userToken remotely
    * then clears the loginService as it was on startup
    */
    setToken(null);
    this.userRole = userRoles.public;
    this.user = {};
    this.isLogged = false;
    $state.go(logoutState);
    },
    resolvePendingState: function (httpPromise) {
    var checkUser = $q.defer(),
    self = this,
    pendingState = self.pendingStateChange;

    // When the $http is done, we register the http result into loginHandler, `data` parameter goes into loginService.loginHandler
    httpPromise.success(self.loginHandler);

    httpPromise.then(
    function success(httpObj) {
    self.doneLoading = true;
    // duplicated logic from $stateChangeStart, slightly different, now we surely have the userRole informations.
    if (pendingState.to.accessLevel === undefined || pendingState.to.accessLevel.bitMask & self.userRole.bitMask) {
    checkUser.resolve();
    } else {
    checkUser.reject('unauthorized');
    }
    },
    function reject(httpObj) {
    checkUser.reject(httpObj.status.toString());
    }
    );
    /**
    * I setted up the state change inside the promises success/error,
    * so i can safely assign pendingStateChange back to null.
    */
    self.pendingStateChange = null;
    return checkUser.promise;
    },
    /**
    * Public properties
    */
    userRole: null,
    user: {},
    isLogged: null,
    pendingStateChange: null,
    doneLoading: null
    };

    getLoginData();
    managePermissions();

    return wrappedService;
    };
    });
    52 changes: 52 additions & 0 deletions urls.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,52 @@
    from django.conf.urls import patterns, include, url
    from django.views.generic import TemplateView
    from rest_framework import routers


    from api import QuestViewSet, TargetViewSet, IdeaViewSet, UserViewSet, GroupViewSet, WItemViewSet, UProductViewSet, ReviewPointViewSet
    from apiauth import ObtainAuthToken, ObtainUser, ObtainLogout

    # Uncomment the next two lines to enable the admin:
    from django.contrib import admin
    admin.autodiscover()



    # Routers provide an easy way of automatically determining the URL conf
    router = routers.DefaultRouter()
    router.register(r'quests', QuestViewSet)
    router.register(r'ideas', IdeaViewSet)
    router.register(r'witems', WItemViewSet)
    router.register(r'reviews', ReviewPointViewSet)
    router.register(r'users', UserViewSet)
    router.register(r'groups', GroupViewSet)
    router.register(r'targets', TargetViewSet)


    urlpatterns = patterns('',
    url(r'^$', TemplateView.as_view(template_name='base.html')),


    # API URLs
    url(r'^api/', include(router.urls)),

    url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),

    # Uncomment the admin/doc line below to enable admin documentation:
    # url(r'^admin/doc/', include('django.contrib.admindocs.urls')),

    # Uncomment the next line to enable the admin:

    url(r'^admin/', include(admin.site.urls)),

    url('', include('social.apps.django_app.urls', namespace='social')),

    url(r"^account/", include("account.urls")),

    url(r'^api-token/login/(?P<backend>[^/]+)/$', ObtainAuthToken.as_view()),

    url(r'^api-token/user/', ObtainUser.as_view()),

    url(r'^api-token/logout/', ObtainLogout.as_view())

    )