Coder Social home page Coder Social logo

Comments (4)

nycholas avatar nycholas commented on June 16, 2024

Once the API Browser has the schema definition, we can overload the UI Template and extends that with some code that will help the API Browser put the JWT Token in the Authorization header. For example,

app.py

browse = JSONRPCBrowe(
   extensions = ['myst_parser'],
   parser = 'markdown',
   ....,
   template_index='./templates/index.html'
)

app = Flask('docstring')
jsonrpc_v1 = JSONRPC(app, '/api/v1', browse=browse)

./templates/index.html

{% extends "index.html" %}
{% block include_script %}
  <script src="//unpkg.com/something@x/some-jwt-bundle.js"></script>
  <script>
    let JWTToken = {};

    // Create an adapter to use the JWT Flask, seems like keycloak-adapter-js

    window.JSONRPCBrowse = JSONRPCBrowse({
        requestInterceptor: (request) => {
          request.headers['Authorization'] = JWTToken.accessToken;
          return request;
        }
      });
  </script>
{% endblock %}
{% endblock %}

It is the generic form, but the API Browse can support the specific provider by extensions, as simple as is:

app.py

browse = JSONRPCBrowe(
   extensions = ['myst_parser', 'flask-jwt'],
   parser = 'markdown',
   ....,
  {'flask_jwt': {
     public_key: '...',
     secret_key: '...',
     ....,
  }}
)

app = Flask('docstring')
jsonrpc_v1 = JSONRPC(app, '/api/v1', browse=browse)

What do you think about that?

from flask-jsonrpc.

Talkless avatar Talkless commented on June 16, 2024

It's a bit hard to get grok of your suggestion, but I guess generally ability to extend templates might work, I guess.

This is some quick-and-dirty hack (without understanding how AngularJS magic works) to make it work, by adding authentication form fields and modifying controllers:

diff --git a/src/flask_jsonrpc/contrib/browse/static/js/apps/browse/controllers.js b/src/flask_jsonrpc/contrib/browse/static/js/apps/browse/controllers.js
index 5fcd9f3..4df5d7a 100644
--- a/src/flask_jsonrpc/contrib/browse/static/js/apps/browse/controllers.js
+++ b/src/flask_jsonrpc/contrib/browse/static/js/apps/browse/controllers.js
@@ -20,6 +20,10 @@
         $scope.response = responseExample;
         $scope.response_object = responseObjectExample;
 
+        window.username = "";
+        window.password = "";
+        window.customer = "";
+
         $scope.$on('App:displayFakeIntro', function(event, display) {
             $scope.showFakeIntro = display;
         });
@@ -108,31 +112,56 @@
         };
     }]);
 
-    App.controller('ResponseObjectCtrl', ['$scope', '$window', '$modal', 'RPC', 'module', function($scope, $window, $modal, RPC, module) {
+    App.controller('ResponseObjectCtrl', ['$scope', '$window', '$modal', 'RPC', 'module', 'serviceUrl', function($scope, $window, $modal, RPC, module, serviceUrl) {
         $scope.module = module;
+        $scope.module.username = $window.username;
+        $scope.module.password = $window.password;
+        $scope.module.customer = $window.customer;
         $scope.$emit('App:displayToolbar', true);
         $scope.$emit('App:breadcrumb', module.name);
 
         var RPCCall = function(module) {
-            var payload = RPC.payload(module);
-            $scope.request_object = payload;
-            $scope.response = undefined;
-            $scope.response_object = undefined;
-            RPC.callWithPayload(payload).success(function(response_object, status, headers, config) { // success
-                var headers_pretty = headers();
-                headers_pretty.data = config.data;
-
-                $scope.response = {status: status, headers: headers_pretty, config: config};
-                $scope.response_object = response_object;
-                $scope.$emit('App:displayContentLoaded', false);
-            }).error(function(response_object, status, headers, config) { // error
-                var headers_pretty = headers();
-                headers_pretty.data = config.data;
 
-                $scope.response = {status_code: status, headers: headers_pretty, config: config};
-                $scope.response_object = response_object;
-                $scope.$emit('App:displayContentLoaded', false);
+            window.username = module.username;
+            window.password = module.password;
+            window.customer = module.customer;
+
+            RPC.generateJWT(module.username, module.password, module.customer).success(function(response_object, status, headers, config) {
+
+                const jwt = response_object['access_token'];
+                console.log("JWT:", jwt);
+
+                var payload = RPC.payload(module);
+                $scope.request_object = payload;
+                $scope.response = undefined;
+                $scope.response_object = undefined;
+
+                RPC.callWithPayload(payload, {method: 'POST', url: serviceUrl, headers: { 'Authorization': "JWT " + jwt }} ).success(function(response_object, status, headers, config) { // success
+                    var headers_pretty = headers();
+                    headers_pretty.data = config.data;
+
+                    $scope.response = {status: status, headers: headers_pretty, config: config};
+                    $scope.response_object = response_object;
+                    $scope.$emit('App:displayContentLoaded', false);
+
+                    }).error(function(response_object, status, headers, config) { // error
+                        var headers_pretty = headers();
+                        headers_pretty.data = config.data;
+
+                        $scope.response = {status_code: status, headers: headers_pretty, config: config};
+                        $scope.response_object = response_object;
+                        $scope.$emit('App:displayContentLoaded', false);
+                });
+
+            }).error(function(response_object, status, headers, config) { // JWT error
+                    var headers_pretty = headers();
+                    headers_pretty.data = config.data;
+
+                    $scope.response = {status_code: status, headers: headers_pretty, config: config};
+                    $scope.response_object = response_object;
+                    $scope.$emit('App:displayContentLoaded', false);
             });
+
         },
         RPCCallModal = function(module) {
             $modal.open({
diff --git a/src/flask_jsonrpc/contrib/browse/static/js/apps/browse/services.js b/src/flask_jsonrpc/contrib/browse/static/js/apps/browse/services.js
index 9b0b287..7a45e59 100644
--- a/src/flask_jsonrpc/contrib/browse/static/js/apps/browse/services.js
+++ b/src/flask_jsonrpc/contrib/browse/static/js/apps/browse/services.js
@@ -116,6 +116,14 @@
 
                     return payload;
                 },
+                generateJWT: function(username, password, customer) {
+                    const options = {
+                        method: 'POST',
+                        url: '/auth',
+                        data: { "username" : customer + ";" + username, "password" : password },
+                    };
+                    return $http(options);
+                },
                 callWithPayload: function(data, options) {
                     var options = options || {method: 'POST', url: serviceUrl};
                     options.data = data;
diff --git a/src/flask_jsonrpc/contrib/browse/templates/browse/partials/response_object.html b/src/flask_jsonrpc/contrib/browse/templates/browse/partials/response_object.html
index 9b57a5e..b8075bc 100644
--- a/src/flask_jsonrpc/contrib/browse/templates/browse/partials/response_object.html
+++ b/src/flask_jsonrpc/contrib/browse/templates/browse/partials/response_object.html
@@ -9,7 +9,14 @@
       <div class="modal-body">
         <h5><b>Summary:</b> <span ng-if="!module.summary">None</span><span style="white-space: pre-wrap;">{{module.summary}}</span></h5>
         <ng-form name="nameDialog" novalidate role="form">
-          <div class="form-group input-group-lg">
+          <div class="form-group input-group-sm">
+            <label for="username">Username</label>
+            <input type="text" class="form-control" autocomplete="on" name="username" id="username" ng-model="module.username" ng-keyup="hitEnter($event)" required>
+            <label for="password">Password</label>
+            <input type="password" class="form-control" autocomplete="on" name="password" id="password" ng-model="module.password" ng-keyup="hitEnter($event)" required>
+            <label for="customer">Customer</label>
+            <input type="text" class="form-control" autocomplete="on" name="customer" id="customer" ng-model="module.customer" ng-keyup="hitEnter($event)" required>
+            <hr/>
             <span ng-repeat="param in module.params">
               <label class="control-label" for="course">{{param.name}} -> {{param.type}}: </label><input type="text" class="form-control" name="{{param.name}}" id="{{param.name}}" ng-model="param.value" ng-keyup="hitEnter($event)" required>
               <span class="help-block"></span>

from flask-jsonrpc.

nycholas avatar nycholas commented on June 16, 2024

It seems to work. One question, Is every request the client needs to ask for a new JWT Token?

For your example, I think the good decision is the approach of API Browser extension to support that.

from flask-jsonrpc.

Talkless avatar Talkless commented on June 16, 2024

It seems to work. One question, Is every request the client needs to ask for a new JWT Token?

No, it does need new token for each request. Simply in order not to check for JWT expiration error (and re-generate transparenlty), I've just made it get a new one on every request. This behavior is only in the Browser of course, not in production. Again, it's a quick-and-very-dirty hack :) .

from flask-jsonrpc.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.