diff --git a/api/config/settings/common.py b/api/config/settings/common.py
index 3000feddb..bf357e17c 100644
--- a/api/config/settings/common.py
+++ b/api/config/settings/common.py
@@ -374,6 +374,7 @@ OAUTH2_PROVIDER = {
"REFRESH_TOKEN_EXPIRE_SECONDS": 3600 * 24 * 15,
"AUTHORIZATION_CODE_EXPIRE_SECONDS": 5 * 60,
"ACCESS_TOKEN_EXPIRE_SECONDS": 60 * 60 * 10,
+ "OAUTH2_SERVER_CLASS": "funkwhale_api.users.oauth.server.OAuth2Server",
}
OAUTH2_PROVIDER_APPLICATION_MODEL = "users.Application"
OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL = "users.AccessToken"
diff --git a/api/funkwhale_api/users/oauth/server.py b/api/funkwhale_api/users/oauth/server.py
new file mode 100644
index 000000000..f62ebf48a
--- /dev/null
+++ b/api/funkwhale_api/users/oauth/server.py
@@ -0,0 +1,25 @@
+import urllib.parse
+import oauthlib.oauth2
+
+
+class OAuth2Server(oauthlib.oauth2.Server):
+ def verify_request(self, uri, *args, **kwargs):
+ valid, request = super().verify_request(uri, *args, **kwargs)
+ if valid:
+ return valid, request
+
+ # maybe the token was given in the querystring?
+ query = urllib.parse.urlparse(request.uri).query
+ token = None
+ if query:
+ parsed_qs = urllib.parse.parse_qs(query)
+ token = parsed_qs.get("token", [])
+ if len(token) > 0:
+ token = token[0]
+
+ if token:
+ valid = self.request_validator.validate_bearer_token(
+ token, request.scopes, request
+ )
+
+ return valid, request
diff --git a/api/tests/test_auth.py b/api/tests/test_auth.py
new file mode 100644
index 000000000..653110f09
--- /dev/null
+++ b/api/tests/test_auth.py
@@ -0,0 +1,36 @@
+from django.urls import reverse
+from rest_framework_jwt.settings import api_settings
+
+jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
+jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
+
+
+def test_can_authenticate_using_jwt_token_param_in_url(factories, preferences, client):
+ user = factories["users.User"]()
+ preferences["common__api_authentication_required"] = True
+ url = reverse("api:v1:tracks-list")
+ response = client.get(url)
+
+ assert response.status_code == 401
+
+ payload = jwt_payload_handler(user)
+ token = jwt_encode_handler(payload)
+ response = client.get(url, data={"jwt": token})
+ assert response.status_code == 200
+
+
+def test_can_authenticate_using_oauth_token_param_in_url(
+ factories, preferences, client, mocker
+):
+ mocker.patch(
+ "funkwhale_api.users.oauth.permissions.should_allow", return_value=True
+ )
+ token = factories["users.AccessToken"]()
+ preferences["common__api_authentication_required"] = True
+ url = reverse("api:v1:tracks-list")
+ response = client.get(url)
+
+ assert response.status_code == 401
+
+ response = client.get(url, data={"token": token.token})
+ assert response.status_code == 200
diff --git a/api/tests/test_jwt_querystring.py b/api/tests/test_jwt_querystring.py
deleted file mode 100644
index 18a673fb4..000000000
--- a/api/tests/test_jwt_querystring.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from django.urls import reverse
-from rest_framework_jwt.settings import api_settings
-
-jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
-jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
-
-
-def test_can_authenticate_using_token_param_in_url(factories, preferences, client):
- user = factories["users.User"]()
- preferences["common__api_authentication_required"] = True
- url = reverse("api:v1:tracks-list")
- response = client.get(url)
-
- assert response.status_code == 401
-
- payload = jwt_payload_handler(user)
- token = jwt_encode_handler(payload)
- response = client.get(url, data={"jwt": token})
- assert response.status_code == 200
diff --git a/front/src/components/auth/ApplicationForm.vue b/front/src/components/auth/ApplicationForm.vue
index 5bd7d3b50..c2eefbfdf 100644
--- a/front/src/components/auth/ApplicationForm.vue
+++ b/front/src/components/auth/ApplicationForm.vue
@@ -77,17 +77,19 @@ import TranslationsMixin from "@/components/mixins/Translations"
export default {
mixins: [TranslationsMixin],
props: {
- app: {type: Object, required: false}
+ app: {type: Object, required: false},
+ defaults: {type: Object, required: false}
},
data() {
let app = this.app || {}
+ let defaults = this.defaults || {}
return {
isLoading: false,
errors: [],
fields: {
- name: app.name || '',
- redirect_uris: app.redirect_uris || 'urn:ietf:wg:oauth:2.0:oob',
- scopes: app.scopes || 'read'
+ name: app.name || defaults.name || '',
+ redirect_uris: app.redirect_uris || defaults.redirect_uris || 'urn:ietf:wg:oauth:2.0:oob',
+ scopes: app.scopes || defaults.scopes || 'read'
},
scopes: [
{id: "profile", icon: 'user'},
diff --git a/front/src/components/auth/ApplicationNew.vue b/front/src/components/auth/ApplicationNew.vue
index 8bb36826c..ba1a575fe 100644
--- a/front/src/components/auth/ApplicationNew.vue
+++ b/front/src/components/auth/ApplicationNew.vue
@@ -9,6 +9,7 @@
Create a new application
@@ -19,6 +20,7 @@
import ApplicationForm from "@/components/auth/ApplicationForm"
export default {
+ props: ['name', 'redirect_uris', 'scopes'],
components: {
ApplicationForm
},
@@ -26,6 +28,11 @@ export default {
return {
application: null,
isLoading: false,
+ defaults: {
+ name: this.name,
+ redirect_uris: this.redirect_uris,
+ scopes: this.scopes,
+ }
}
},
computed: {
diff --git a/front/src/router/index.js b/front/src/router/index.js
index 7b97cbe51..182cf61b0 100644
--- a/front/src/router/index.js
+++ b/front/src/router/index.js
@@ -104,6 +104,11 @@ export default new Router({
{
path: '/settings/applications/new',
name: 'settings.applications.new',
+ props: (route) => ({
+ scopes: route.query.scopes,
+ name: route.query.name,
+ redirect_uris: route.query.redirect_uris,
+ }),
component: () =>
import(/* webpackChunkName: "core" */ "@/components/auth/ApplicationNew"),
},