@typekcz-nocobase-plugins/plugin-oidc-plus 1.0.3 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/dist/client/index.js +1 -1
  2. package/dist/externalVersion.js +9 -9
  3. package/package.json +1 -1
  4. package/src/client/OIDCButton.tsx +70 -0
  5. package/src/client/Options.tsx +359 -0
  6. package/src/client/index.tsx +19 -0
  7. package/src/client/locale/index.ts +18 -0
  8. package/src/constants.ts +7 -0
  9. package/src/index.ts +2 -0
  10. package/src/locale/en-US.json +40 -0
  11. package/src/locale/es-ES.json +25 -0
  12. package/src/locale/fr-FR.json +21 -0
  13. package/src/locale/ko_KR.json +28 -0
  14. package/src/locale/pt-BR.json +21 -0
  15. package/src/locale/zh-CN.json +28 -0
  16. package/src/server/__tests__/oidc.test.ts +283 -0
  17. package/src/server/actions/getAuthUrl.ts +25 -0
  18. package/src/server/actions/redirect.ts +32 -0
  19. package/src/server/index.ts +1 -0
  20. package/src/server/oidc-auth.ts +169 -0
  21. package/src/server/plugin.ts +63 -0
  22. package/src/swagger/index.ts +157 -0
  23. package/dist/node_modules/nanoid/.devcontainer.json +0 -23
  24. package/dist/node_modules/nanoid/LICENSE +0 -20
  25. package/dist/node_modules/nanoid/async/index.browser.cjs +0 -69
  26. package/dist/node_modules/nanoid/async/index.browser.js +0 -69
  27. package/dist/node_modules/nanoid/async/index.cjs +0 -71
  28. package/dist/node_modules/nanoid/async/index.d.ts +0 -56
  29. package/dist/node_modules/nanoid/async/index.js +0 -71
  30. package/dist/node_modules/nanoid/async/index.native.js +0 -57
  31. package/dist/node_modules/nanoid/async/package.json +0 -12
  32. package/dist/node_modules/nanoid/bin/nanoid.cjs +0 -55
  33. package/dist/node_modules/nanoid/index.browser.cjs +0 -72
  34. package/dist/node_modules/nanoid/index.browser.js +0 -72
  35. package/dist/node_modules/nanoid/index.cjs +0 -1
  36. package/dist/node_modules/nanoid/index.d.cts +0 -91
  37. package/dist/node_modules/nanoid/index.d.ts +0 -91
  38. package/dist/node_modules/nanoid/index.js +0 -85
  39. package/dist/node_modules/nanoid/nanoid.js +0 -1
  40. package/dist/node_modules/nanoid/non-secure/index.cjs +0 -34
  41. package/dist/node_modules/nanoid/non-secure/index.d.ts +0 -33
  42. package/dist/node_modules/nanoid/non-secure/index.js +0 -34
  43. package/dist/node_modules/nanoid/non-secure/package.json +0 -6
  44. package/dist/node_modules/nanoid/package.json +0 -1
  45. package/dist/node_modules/nanoid/url-alphabet/index.cjs +0 -7
  46. package/dist/node_modules/nanoid/url-alphabet/index.js +0 -7
  47. package/dist/node_modules/nanoid/url-alphabet/package.json +0 -6
  48. package/dist/node_modules/openid-client/lib/client.js +0 -1884
  49. package/dist/node_modules/openid-client/lib/device_flow_handle.js +0 -125
  50. package/dist/node_modules/openid-client/lib/errors.js +0 -55
  51. package/dist/node_modules/openid-client/lib/helpers/assert.js +0 -24
  52. package/dist/node_modules/openid-client/lib/helpers/base64url.js +0 -13
  53. package/dist/node_modules/openid-client/lib/helpers/client.js +0 -208
  54. package/dist/node_modules/openid-client/lib/helpers/consts.js +0 -7
  55. package/dist/node_modules/openid-client/lib/helpers/decode_jwt.js +0 -27
  56. package/dist/node_modules/openid-client/lib/helpers/deep_clone.js +0 -1
  57. package/dist/node_modules/openid-client/lib/helpers/defaults.js +0 -27
  58. package/dist/node_modules/openid-client/lib/helpers/generators.js +0 -14
  59. package/dist/node_modules/openid-client/lib/helpers/is_key_object.js +0 -4
  60. package/dist/node_modules/openid-client/lib/helpers/is_plain_object.js +0 -1
  61. package/dist/node_modules/openid-client/lib/helpers/issuer.js +0 -111
  62. package/dist/node_modules/openid-client/lib/helpers/keystore.js +0 -298
  63. package/dist/node_modules/openid-client/lib/helpers/merge.js +0 -24
  64. package/dist/node_modules/openid-client/lib/helpers/pick.js +0 -9
  65. package/dist/node_modules/openid-client/lib/helpers/process_response.js +0 -71
  66. package/dist/node_modules/openid-client/lib/helpers/request.js +0 -200
  67. package/dist/node_modules/openid-client/lib/helpers/unix_timestamp.js +0 -1
  68. package/dist/node_modules/openid-client/lib/helpers/weak_cache.js +0 -1
  69. package/dist/node_modules/openid-client/lib/helpers/webfinger_normalize.js +0 -71
  70. package/dist/node_modules/openid-client/lib/helpers/www_authenticate_parser.js +0 -14
  71. package/dist/node_modules/openid-client/lib/index.js +0 -1
  72. package/dist/node_modules/openid-client/lib/issuer.js +0 -192
  73. package/dist/node_modules/openid-client/lib/issuer_registry.js +0 -3
  74. package/dist/node_modules/openid-client/lib/passport_strategy.js +0 -205
  75. package/dist/node_modules/openid-client/lib/token_set.js +0 -35
  76. package/dist/node_modules/openid-client/package.json +0 -1
  77. package/dist/node_modules/openid-client/types/index.d.ts +0 -623
@@ -7,4 +7,4 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
 
10
- !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("react-i18next"),require("@nocobase/plugin-auth/client"),require("@nocobase/client"),require("antd"),require("@ant-design/icons"),require("@formily/react"),require("react-router-dom"),require("react"),require("@formily/antd-v5")):"function"==typeof define&&define.amd?define("@typekcz-nocobase-plugins/plugin-oidc-plus",["react-i18next","@nocobase/plugin-auth/client","@nocobase/client","antd","@ant-design/icons","@formily/react","react-router-dom","react","@formily/antd-v5"],t):"object"==typeof exports?exports["@typekcz-nocobase-plugins/plugin-oidc-plus"]=t(require("react-i18next"),require("@nocobase/plugin-auth/client"),require("@nocobase/client"),require("antd"),require("@ant-design/icons"),require("@formily/react"),require("react-router-dom"),require("react"),require("@formily/antd-v5")):e["@typekcz-nocobase-plugins/plugin-oidc-plus"]=t(e["react-i18next"],e["@nocobase/plugin-auth/client"],e["@nocobase/client"],e.antd,e["@ant-design/icons"],e["@formily/react"],e["react-router-dom"],e.react,e["@formily/antd-v5"])}(self,function(e,t,r,o,n,i,a,c,u){return function(){"use strict";var l={482:function(e){e.exports=n},632:function(e){e.exports=u},505:function(e){e.exports=i},772:function(e){e.exports=r},689:function(e){e.exports=t},721:function(e){e.exports=o},156:function(e){e.exports=c},238:function(t){t.exports=e},128:function(e){e.exports=a}},p={};function s(e){var t=p[e];if(void 0!==t)return t.exports;var r=p[e]={exports:{}};return l[e](r,r.exports,s),r.exports}s.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return s.d(t,{a:t}),t},s.d=function(e,t){for(var r in t)s.o(t,r)&&!s.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},s.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},s.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var d={};s.r(d),s.d(d,{PluginOIDCClient:function(){return N},default:function(){return L}});var f=s("772"),m=s("689"),y=s.n(m),b="tnp_oidc_plus_logout",v=s("482"),h=s("721"),x=s("156"),g=s.n(x),S=s("238"),I="oidc";function w(e){return f.i18n.t(e,{ns:I})}function T(){return(0,S.useTranslation)(I)}var P=s("128");function F(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var o in r)e[o]=r[o]}return e}var k=function e(t,r){function o(e,o,n){if("undefined"!=typeof document){"number"==typeof(n=F({},r,n)).expires&&(n.expires=new Date(Date.now()+864e5*n.expires)),n.expires&&(n.expires=n.expires.toUTCString()),e=encodeURIComponent(e).replace(/%(2[346B]|5E|60|7C)/g,decodeURIComponent).replace(/[()]/g,escape);var i="";for(var a in n){if(!n[a])continue;if(i+="; "+a,!0!==n[a])i+="="+n[a].split(";")[0]}return document.cookie=e+"="+t.write(o,e)+i}}return Object.create({set:o,get:function(e){if("undefined"!=typeof document&&(!arguments.length||!!e)){for(var r=document.cookie?document.cookie.split("; "):[],o={},n=0;n<r.length;n++){var i=r[n].split("="),a=i.slice(1).join("=");try{var c=decodeURIComponent(i[0]);if(o[c]=t.read(a,c),e===c)break}catch(e){}}return e?o[e]:o}},remove:function(e,t){o(e,"",F({},t,{expires:-1}))},withAttributes:function(t){return e(this.converter,F({},this.attributes,t))},withConverter:function(t){return e(F({},this.converter,t),this.attributes)}},{attributes:{value:Object.freeze(r)},converter:{value:Object.freeze(t)}})}({read:function(e){return'"'===e[0]&&(e=e.slice(1,-1)),e.replace(/(%[\dA-F]{2})+/gi,decodeURIComponent)},write:function(e){return encodeURIComponent(e).replace(/%(2[346BF]|3[AC-F]|40|5[BDE]|60|7[BCD])/g,decodeURIComponent)}},{path:"/"});function C(e,t,r,o,n,i,a){try{var c=e[i](a),u=c.value}catch(e){r(e);return}c.done?t(u):Promise.resolve(u).then(o,n)}function O(){var e,t,r=(e=["\n display: flex;\n "],!t&&(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}})));return O=function(){return r},r}var E=function(e){var t,r,o=e.authenticator,n=T().t,i=(0,f.useAPIClient)(),a=new URLSearchParams((0,P.useLocation)().search),c=a.get("redirect");var u=(r=(t=function(){var e,t,r;return function(e,t){var r,o,n,i,a={label:0,sent:function(){if(1&n[0])throw n[1];return n[1]},trys:[],ops:[]};return i={next:c(0),throw:c(1),return:c(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function c(i){return function(c){return function(i){if(r)throw TypeError("Generator is already executing.");for(;a;)try{if(r=1,o&&(n=2&i[0]?o.return:i[0]?o.throw||((n=o.return)&&n.call(o),0):o.next)&&!(n=n.call(o,i[1])).done)return n;switch(o=0,n&&(i=[2&i[0],n.value]),i[0]){case 0:case 1:n=i;break;case 4:return a.label++,{value:i[1],done:!1};case 5:a.label++,o=i[1],i=[0];continue;case 7:i=a.ops.pop(),a.trys.pop();continue;default:if(!(n=(n=a.trys).length>0&&n[n.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!n||i[1]>n[0]&&i[1]<n[3])){a.label=i[1];break}if(6===i[0]&&a.label<n[1]){a.label=n[1],n=i;break}if(n&&a.label<n[2]){a.label=n[2],a.ops.push(i);break}n[2]&&a.ops.pop(),a.trys.pop();continue}i=t.call(e,a)}catch(e){i=[6,e],o=0}finally{r=n=0}if(5&i[0])throw i[1];return{value:i[0]?i[1]:void 0,done:!0}}([i,c])}}}(this,function(n){switch(n.label){case 0:return[4,i.request({method:"post",url:"oidc:getAuthUrl",headers:{"X-Authenticator":o.name},data:{redirect:c}})];case 1:return r=null==(t=n.sent())?void 0:null===(e=t.data)||void 0===e?void 0:e.data,window.location.replace(r),[2]}})},function(){var e=this,r=arguments;return new Promise(function(o,n){var i=t.apply(e,r);function a(e){C(i,o,n,a,c,"next",e)}function c(e){C(i,o,n,a,c,"throw",e)}a(void 0)})}),function(){return r.apply(this,arguments)});return(0,x.useEffect)(function(){var e=k.get(b);if(e){var t=new URL(e);t.searchParams.set("post_logout_redirect_uri",window.location.href),k.remove(b,{domain:window.location.hostname}),window.location.href=t.href}var r=a.get("authenticator"),i=a.get("error");if(r===o.name){if(i){h.message.error(n(i));return}}}),g().createElement(h.Space,{direction:"vertical",className:(0,f.css)(O())},g().createElement(h.Button,{shape:"round",block:!0,icon:g().createElement(v.LoginOutlined,null),onClick:u},n(o.title)))},j=s("632"),R=s("505"),q={type:"object",properties:{public:{type:"object",properties:{autoSignup:{"x-decorator":"FormItem",type:"boolean",title:'{{t("Sign up automatically when the user does not exist")}}',"x-component":"Checkbox",default:!0}}},oidc:{type:"object",properties:{collapse:{type:"void","x-component":"FormTab",properties:{basic:{type:"void","x-component":"FormTab.TabPane","x-component-props":{tab:w("Basic configuration")},properties:{issuer:{type:"string",title:'{{t("Issuer")}}',"x-component":"Input","x-decorator":"FormItem",required:!0},clientId:{type:"string",title:'{{t("Client ID")}}',"x-component":"Input","x-decorator":"FormItem",required:!0},clientSecret:{type:"string",title:'{{t("Client Secret")}}',"x-component":"Input","x-decorator":"FormItem",required:!0},scope:{type:"string",title:'{{t("scope")}}',"x-component":"Input","x-decorator":"FormItem","x-decorator-props":{tooltip:'{{t("Default: openid profile email")}}'}},idTokenSignedResponseAlg:{type:"string",title:'{{t("id_token signed response algorithm")}}',"x-component":"Select","x-decorator":"FormItem",enum:[{label:"HS256",value:"HS256"},{label:"HS384",value:"HS384"},{label:"HS512",value:"HS512"},{label:"RS256",value:"RS256"},{label:"RS384",value:"RS384"},{label:"RS512",value:"RS512"},{label:"ES256",value:"ES256"},{label:"ES384",value:"ES384"},{label:"ES512",value:"ES512"},{label:"PS256",value:"PS256"},{label:"PS384",value:"PS384"},{label:"PS512",value:"PS512"}]}}},mapping:{type:"void","x-component":"FormTab.TabPane","x-component-props":{tab:w("Field mapping")},properties:{fieldMap:{title:'{{t("Field Map")}}',type:"array","x-decorator":"FormItem","x-component":"ArrayItems",items:{type:"object","x-decorator":"ArrayItems.Item",properties:{space:{type:"void","x-component":"Space",properties:{source:{type:"string","x-decorator":"FormItem","x-component":"Input","x-component-props":{placeholder:'{{t("source")}}'}},target:{type:"string","x-decorator":"FormItem","x-component":"Input","x-component-props":{placeholder:'{{t("target")}}'}},remove:{type:"void","x-decorator":"FormItem","x-component":"ArrayItems.Remove"}}}}},properties:{add:{type:"void",title:"Add","x-component":"ArrayItems.Addition"}}},userBindField:{type:"string",title:'{{t("Use this field to bind the user")}}',"x-component":"Select","x-decorator":"FormItem",default:"email",enum:[{label:w("Email"),value:"email"},{label:w("Username"),value:"username"}],required:!0}}},advanced:{type:"void","x-component":"FormTab.TabPane","x-component-props":{tab:w("Advanced configuration")},properties:{logout:{type:"boolean",title:'{{t("RP-initiated logout")}}',"x-component":"Checkbox","x-decorator":"FormItem","x-decorator-props":{tooltip:'{{t("Performs logout on the issuer (uses end_session_endpoint in the issuer configuration)")}}'}},http:{type:"boolean",title:'{{t("HTTP")}}',"x-component":"Checkbox","x-decorator":"FormItem","x-decorator-props":{tooltip:'{{t("Check if NocoBase is running on HTTP protocol")}}'}},port:{type:"number",title:'{{t("Port")}}',"x-component":"InputNumber","x-decorator":"FormItem","x-decorator-props":{tooltip:'{{t("The port number of the NocoBase service if it is not 80 or 443")}}'},"x-component-props":{style:{width:"15%",minWidth:"100px"}}},stateToken:{type:"string",title:'{{t("State token")}}',"x-component":"Input","x-decorator":"FormItem",description:w("The state token helps prevent CSRF attacks. It's recommended to leave it blank for automatic random generation.")},exchangeBodyKeys:{type:"array",title:'{{t("Pass parameters in the authorization code grant exchange")}}',"x-decorator":"FormItem","x-component":"ArrayItems",default:[{paramName:"",optionsKey:"clientId"},{paramName:"",optionsKey:"clientSecret"}],items:{type:"object","x-decorator":"ArrayItems.Item",properties:{space:{type:"void","x-component":"Space",properties:{enabled:{type:"boolean","x-decorator":"FormItem","x-component":"Checkbox"},optionsKey:{type:"string","x-decorator":"FormItem","x-decorator-props":{style:{width:"100px"}},"x-component":"Select","x-read-pretty":!0,enum:[{label:w("Client ID"),value:"clientId"},{label:w("Client Secret"),value:"clientSecret"}]},paramName:{type:"string","x-decorator":"FormItem","x-component":"Input","x-component-props":{placeholder:'{{t("Parameter name")}}'}}}}}}},userInfoMethod:{type:"string",title:'{{t("Method to call the user info endpoint")}}',"x- decorator":"FormItem","x-component":"Radio.Group",default:"GET",enum:[{label:"GET",value:"GET"},{label:"POST",value:"POST"}],"x-reactions":[{dependencies:[".accessTokenVia"],when:'{{$deps[0] === "query"}}',fulfill:{state:{value:"GET"}}},{dependencies:[".accessTokenVia"],when:'{{$deps[0] === "body"}}',fulfill:{state:{value:"POST"}}}]},accessTokenVia:{type:"string",title:'{{t("Where to put the access token when calling the user info endpoint")}}',"x- decorator":"FormItem","x-component":"Radio.Group",default:"header",enum:[{label:w("Header"),value:"header"},{label:w("Body (Use with POST method)"),value:"body"},{label:w("Query parameters (Use with GET method)"),value:"query"}]}}}}}}},usage:{type:"void","x-component":"Usage"}}},A=(0,R.observer)(function(){var e=T().t,t=(0,f.useApp)(),r=(0,x.useMemo)(function(){return t.getApiUrl("oidc:redirect")},[t]),o=function(t){navigator.clipboard.writeText(t),h.message.success(e("Copied"))};return g().createElement(h.Card,{title:e("Usage"),type:"inner"},g().createElement(f.FormItem,{label:e("Redirect URL")},g().createElement(f.Input,{value:r,disabled:!0,addonBefore:g().createElement(v.CopyOutlined,{onClick:function(){return o(r)}})})))},{displayName:"Usage"}),_=function(){var e=T().t;return g().createElement(f.SchemaComponent,{scope:{t:e},components:{Usage:A,ArrayItems:j.ArrayItems,Space:h.Space,FormTab:j.FormTab},schema:q})};function U(e,t,r,o,n,i,a){try{var c=e[i](a),u=c.value}catch(e){r(e);return}c.done?t(u):Promise.resolve(u).then(o,n)}function B(e,t,r){return(B=z()?Reflect.construct:function(e,t,r){var o=[null];o.push.apply(o,t);var n=new(Function.bind.apply(e,o));return r&&G(n,r.prototype),n}).apply(null,arguments)}function M(e,t){for(var r=0;r<t.length;r++){var o=t[r];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(e,o.key,o)}}function D(e){return(D=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function G(e,t){return(G=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function H(e){var t="function"==typeof Map?new Map:void 0;return(H=function(e){var r;if(null===e||(r=e,-1===Function.toString.call(r).indexOf("[native code]")))return e;if("function"!=typeof e)throw TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,o)}function o(){return B(e,arguments,D(this).constructor)}return o.prototype=Object.create(e.prototype,{constructor:{value:o,enumerable:!1,writable:!0,configurable:!0}}),G(o,e)})(e)}function z(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){}))}catch(e){}return(z=function(){return!!e})()}var N=function(e){var t,r,o;function n(){var e,t,r;return!function(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}(this,n),e=this,t=n,r=arguments,t=D(t),function(e,t){return t&&("object"===function(e){return e&&"undefined"!=typeof Symbol&&e.constructor===Symbol?"symbol":typeof e}(t)||"function"==typeof t)?t:function(e){if(void 0===e)throw ReferenceError("this hasn't been initialised - super() hasn't been called");return e}(e)}(e,z()?Reflect.construct(t,r||[],D(e).constructor):t.apply(e,r))}return!function(e,t){if("function"!=typeof t&&null!==t)throw TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&G(e,t)}(n,e),t=n,r=[{key:"load",value:function(){var e,t=this;return(e=function(){return function(e,t){var r,o,n,i,a={label:0,sent:function(){if(1&n[0])throw n[1];return n[1]},trys:[],ops:[]};return i={next:c(0),throw:c(1),return:c(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function c(i){return function(c){return function(i){if(r)throw TypeError("Generator is already executing.");for(;a;)try{if(r=1,o&&(n=2&i[0]?o.return:i[0]?o.throw||((n=o.return)&&n.call(o),0):o.next)&&!(n=n.call(o,i[1])).done)return n;switch(o=0,n&&(i=[2&i[0],n.value]),i[0]){case 0:case 1:n=i;break;case 4:return a.label++,{value:i[1],done:!1};case 5:a.label++,o=i[1],i=[0];continue;case 7:i=a.ops.pop(),a.trys.pop();continue;default:if(!(n=(n=a.trys).length>0&&n[n.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!n||i[1]>n[0]&&i[1]<n[3])){a.label=i[1];break}if(6===i[0]&&a.label<n[1]){a.label=n[1],n=i;break}if(n&&a.label<n[2]){a.label=n[2],a.ops.push(i);break}n[2]&&a.ops.pop(),a.trys.pop();continue}i=t.call(e,a)}catch(e){i=[6,e],o=0}finally{r=n=0}if(5&i[0])throw i[1];return{value:i[0]?i[1]:void 0,done:!0}}([i,c])}}}(this,function(e){return t.app.pm.get(y()).registerType("OIDC+",{components:{SignInButton:E,AdminSettingsForm:_}}),[2]})},function(){var t=this,r=arguments;return new Promise(function(o,n){var i=e.apply(t,r);function a(e){U(i,o,n,a,c,"next",e)}function c(e){U(i,o,n,a,c,"throw",e)}a(void 0)})})()}}],M(t.prototype,r),n}(H(f.Plugin)),L=N;return d}()});
10
+ !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("react-i18next"),require("@nocobase/plugin-auth/client"),require("@nocobase/client"),require("react-router-dom"),require("react"),require("@formily/antd-v5"),require("@formily/react"),require("@ant-design/icons"),require("antd")):"function"==typeof define&&define.amd?define("@typekcz-nocobase-plugins/plugin-oidc-plus",["react-i18next","@nocobase/plugin-auth/client","@nocobase/client","react-router-dom","react","@formily/antd-v5","@formily/react","@ant-design/icons","antd"],t):"object"==typeof exports?exports["@typekcz-nocobase-plugins/plugin-oidc-plus"]=t(require("react-i18next"),require("@nocobase/plugin-auth/client"),require("@nocobase/client"),require("react-router-dom"),require("react"),require("@formily/antd-v5"),require("@formily/react"),require("@ant-design/icons"),require("antd")):e["@typekcz-nocobase-plugins/plugin-oidc-plus"]=t(e["react-i18next"],e["@nocobase/plugin-auth/client"],e["@nocobase/client"],e["react-router-dom"],e.react,e["@formily/antd-v5"],e["@formily/react"],e["@ant-design/icons"],e.antd)}(self,function(e,t,r,o,n,i,a,c,u){return function(){"use strict";var l={482:function(e){e.exports=c},632:function(e){e.exports=i},505:function(e){e.exports=a},772:function(e){e.exports=r},689:function(e){e.exports=t},721:function(e){e.exports=u},156:function(e){e.exports=n},238:function(t){t.exports=e},128:function(e){e.exports=o}},p={};function s(e){var t=p[e];if(void 0!==t)return t.exports;var r=p[e]={exports:{}};return l[e](r,r.exports,s),r.exports}s.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return s.d(t,{a:t}),t},s.d=function(e,t){for(var r in t)s.o(t,r)&&!s.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},s.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},s.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var d={};return!function(){s.r(d),s.d(d,{default:function(){return R},PluginOIDCClient:function(){return j}});var e=s(772),t=s(689),r=s.n(t),o="tnp_oidc_plus_logout",n=s(482),i=s(721),a=s(156),c=s.n(a),u=s(238),l="oidc";function p(t){return e.i18n.t(t,{ns:l})}function f(){return(0,u.useTranslation)(l)}var m=s(128);function y(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var o in r)e[o]=r[o]}return e}var b=function e(t,r){function o(e,o,n){if("undefined"!=typeof document){"number"==typeof(n=y({},r,n)).expires&&(n.expires=new Date(Date.now()+864e5*n.expires)),n.expires&&(n.expires=n.expires.toUTCString()),e=encodeURIComponent(e).replace(/%(2[346B]|5E|60|7C)/g,decodeURIComponent).replace(/[()]/g,escape);var i="";for(var a in n)n[a]&&(i+="; "+a,!0!==n[a]&&(i+="="+n[a].split(";")[0]));return document.cookie=e+"="+t.write(o,e)+i}}return Object.create({set:o,get:function(e){if("undefined"!=typeof document&&(!arguments.length||e)){for(var r=document.cookie?document.cookie.split("; "):[],o={},n=0;n<r.length;n++){var i=r[n].split("="),a=i.slice(1).join("=");try{var c=decodeURIComponent(i[0]);if(o[c]=t.read(a,c),e===c)break}catch(e){}}return e?o[e]:o}},remove:function(e,t){o(e,"",y({},t,{expires:-1}))},withAttributes:function(t){return e(this.converter,y({},this.attributes,t))},withConverter:function(t){return e(y({},this.converter,t),this.attributes)}},{attributes:{value:Object.freeze(r)},converter:{value:Object.freeze(t)}})}({read:function(e){return'"'===e[0]&&(e=e.slice(1,-1)),e.replace(/(%[\dA-F]{2})+/gi,decodeURIComponent)},write:function(e){return encodeURIComponent(e).replace(/%(2[346BF]|3[AC-F]|40|5[BDE]|60|7[BCD])/g,decodeURIComponent)}},{path:"/"});function v(e,t,r,o,n,i,a){try{var c=e[i](a),u=c.value}catch(e){r(e);return}c.done?t(u):Promise.resolve(u).then(o,n)}function h(){var e,t,r=(e=["\n display: flex;\n "],t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}})));return h=function(){return r},r}var x=function(t){var r,u,l=t.authenticator,p=f().t,s=(0,e.useAPIClient)(),d=new URLSearchParams((0,m.useLocation)().search),y=d.get("redirect"),x=(r=function(){var e,t,r;return function(e,t){var r,o,n,i,a={label:0,sent:function(){if(1&n[0])throw n[1];return n[1]},trys:[],ops:[]};return i={next:c(0),throw:c(1),return:c(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function c(i){return function(c){var u=[i,c];if(r)throw TypeError("Generator is already executing.");for(;a;)try{if(r=1,o&&(n=2&u[0]?o.return:u[0]?o.throw||((n=o.return)&&n.call(o),0):o.next)&&!(n=n.call(o,u[1])).done)return n;switch(o=0,n&&(u=[2&u[0],n.value]),u[0]){case 0:case 1:n=u;break;case 4:return a.label++,{value:u[1],done:!1};case 5:a.label++,o=u[1],u=[0];continue;case 7:u=a.ops.pop(),a.trys.pop();continue;default:if(!(n=(n=a.trys).length>0&&n[n.length-1])&&(6===u[0]||2===u[0])){a=0;continue}if(3===u[0]&&(!n||u[1]>n[0]&&u[1]<n[3])){a.label=u[1];break}if(6===u[0]&&a.label<n[1]){a.label=n[1],n=u;break}if(n&&a.label<n[2]){a.label=n[2],a.ops.push(u);break}n[2]&&a.ops.pop(),a.trys.pop();continue}u=t.call(e,a)}catch(e){u=[6,e],o=0}finally{r=n=0}if(5&u[0])throw u[1];return{value:u[0]?u[1]:void 0,done:!0}}}}(this,function(o){switch(o.label){case 0:return[4,s.request({method:"post",url:"oidc:getAuthUrl",headers:{"X-Authenticator":l.name},data:{redirect:y}})];case 1:return r=null==(t=o.sent())||null==(e=t.data)?void 0:e.data,window.location.replace(r),[2]}})},u=function(){var e=this,t=arguments;return new Promise(function(o,n){var i=r.apply(e,t);function a(e){v(i,o,n,a,c,"next",e)}function c(e){v(i,o,n,a,c,"throw",e)}a(void 0)})},function(){return u.apply(this,arguments)});return(0,a.useEffect)(function(){var e=b.get(o);if(e){var t=new URL(e);t.searchParams.set("post_logout_redirect_uri",window.location.href),b.remove(o,{domain:window.location.hostname}),window.location.href=t.href}var r=d.get("authenticator"),n=d.get("error");if(r===l.name&&n)return void i.message.error(p(n))}),c().createElement(i.Space,{direction:"vertical",className:(0,e.css)(h())},c().createElement(i.Button,{shape:"round",block:!0,icon:c().createElement(n.LoginOutlined,null),onClick:x},p(l.title)))},g=s(632),S=s(505),I={type:"object",properties:{public:{type:"object",properties:{autoSignup:{"x-decorator":"FormItem",type:"boolean",title:'{{t("Sign up automatically when the user does not exist")}}',"x-component":"Checkbox",default:!0}}},oidc:{type:"object",properties:{collapse:{type:"void","x-component":"FormTab",properties:{basic:{type:"void","x-component":"FormTab.TabPane","x-component-props":{tab:p("Basic configuration")},properties:{issuer:{type:"string",title:'{{t("Issuer")}}',"x-component":"Input","x-decorator":"FormItem",required:!0},clientId:{type:"string",title:'{{t("Client ID")}}',"x-component":"Input","x-decorator":"FormItem",required:!0},clientSecret:{type:"string",title:'{{t("Client Secret")}}',"x-component":"Input","x-decorator":"FormItem",required:!0},scope:{type:"string",title:'{{t("scope")}}',"x-component":"Input","x-decorator":"FormItem","x-decorator-props":{tooltip:'{{t("Default: openid profile email")}}'}},idTokenSignedResponseAlg:{type:"string",title:'{{t("id_token signed response algorithm")}}',"x-component":"Select","x-decorator":"FormItem",enum:[{label:"HS256",value:"HS256"},{label:"HS384",value:"HS384"},{label:"HS512",value:"HS512"},{label:"RS256",value:"RS256"},{label:"RS384",value:"RS384"},{label:"RS512",value:"RS512"},{label:"ES256",value:"ES256"},{label:"ES384",value:"ES384"},{label:"ES512",value:"ES512"},{label:"PS256",value:"PS256"},{label:"PS384",value:"PS384"},{label:"PS512",value:"PS512"}]}}},mapping:{type:"void","x-component":"FormTab.TabPane","x-component-props":{tab:p("Field mapping")},properties:{fieldMap:{title:'{{t("Field Map")}}',type:"array","x-decorator":"FormItem","x-component":"ArrayItems",items:{type:"object","x-decorator":"ArrayItems.Item",properties:{space:{type:"void","x-component":"Space",properties:{source:{type:"string","x-decorator":"FormItem","x-component":"Input","x-component-props":{placeholder:'{{t("source")}}'}},target:{type:"string","x-decorator":"FormItem","x-component":"Input","x-component-props":{placeholder:'{{t("target")}}'}},remove:{type:"void","x-decorator":"FormItem","x-component":"ArrayItems.Remove"}}}}},properties:{add:{type:"void",title:"Add","x-component":"ArrayItems.Addition"}}},userBindField:{type:"string",title:'{{t("Use this field to bind the user")}}',"x-component":"Select","x-decorator":"FormItem",default:"email",enum:[{label:p("Email"),value:"email"},{label:p("Username"),value:"username"}],required:!0}}},advanced:{type:"void","x-component":"FormTab.TabPane","x-component-props":{tab:p("Advanced configuration")},properties:{logout:{type:"boolean",title:'{{t("RP-initiated logout")}}',"x-component":"Checkbox","x-decorator":"FormItem","x-decorator-props":{tooltip:'{{t("Performs logout on the issuer (uses end_session_endpoint in the issuer configuration)")}}'}},http:{type:"boolean",title:'{{t("HTTP")}}',"x-component":"Checkbox","x-decorator":"FormItem","x-decorator-props":{tooltip:'{{t("Check if NocoBase is running on HTTP protocol")}}'}},port:{type:"number",title:'{{t("Port")}}',"x-component":"InputNumber","x-decorator":"FormItem","x-decorator-props":{tooltip:'{{t("The port number of the NocoBase service if it is not 80 or 443")}}'},"x-component-props":{style:{width:"15%",minWidth:"100px"}}},stateToken:{type:"string",title:'{{t("State token")}}',"x-component":"Input","x-decorator":"FormItem",description:p("The state token helps prevent CSRF attacks. It's recommended to leave it blank for automatic random generation.")},exchangeBodyKeys:{type:"array",title:'{{t("Pass parameters in the authorization code grant exchange")}}',"x-decorator":"FormItem","x-component":"ArrayItems",default:[{paramName:"",optionsKey:"clientId"},{paramName:"",optionsKey:"clientSecret"}],items:{type:"object","x-decorator":"ArrayItems.Item",properties:{space:{type:"void","x-component":"Space",properties:{enabled:{type:"boolean","x-decorator":"FormItem","x-component":"Checkbox"},optionsKey:{type:"string","x-decorator":"FormItem","x-decorator-props":{style:{width:"100px"}},"x-component":"Select","x-read-pretty":!0,enum:[{label:p("Client ID"),value:"clientId"},{label:p("Client Secret"),value:"clientSecret"}]},paramName:{type:"string","x-decorator":"FormItem","x-component":"Input","x-component-props":{placeholder:'{{t("Parameter name")}}'}}}}}}},userInfoMethod:{type:"string",title:'{{t("Method to call the user info endpoint")}}',"x- decorator":"FormItem","x-component":"Radio.Group",default:"GET",enum:[{label:"GET",value:"GET"},{label:"POST",value:"POST"}],"x-reactions":[{dependencies:[".accessTokenVia"],when:'{{$deps[0] === "query"}}',fulfill:{state:{value:"GET"}}},{dependencies:[".accessTokenVia"],when:'{{$deps[0] === "body"}}',fulfill:{state:{value:"POST"}}}]},accessTokenVia:{type:"string",title:'{{t("Where to put the access token when calling the user info endpoint")}}',"x- decorator":"FormItem","x-component":"Radio.Group",default:"header",enum:[{label:p("Header"),value:"header"},{label:p("Body (Use with POST method)"),value:"body"},{label:p("Query parameters (Use with GET method)"),value:"query"}]}}}}}}},usage:{type:"void","x-component":"Usage"}}},w=(0,S.observer)(function(){var t=f().t,r=(0,e.useApp)(),o=(0,a.useMemo)(function(){return r.getApiUrl("oidc:redirect")},[r]),u=function(e){navigator.clipboard.writeText(e),i.message.success(t("Copied"))};return c().createElement(i.Card,{title:t("Usage"),type:"inner"},c().createElement(e.FormItem,{label:t("Redirect URL")},c().createElement(e.Input,{value:o,disabled:!0,addonBefore:c().createElement(n.CopyOutlined,{onClick:function(){return u(o)}})})))},{displayName:"Usage"}),T=function(){var t=f().t;return c().createElement(e.SchemaComponent,{scope:{t:t},components:{Usage:w,ArrayItems:g.ArrayItems,Space:i.Space,FormTab:g.FormTab},schema:I})};function P(e,t,r,o,n,i,a){try{var c=e[i](a),u=c.value}catch(e){r(e);return}c.done?t(u):Promise.resolve(u).then(o,n)}function F(e,t,r){return(F=E()?Reflect.construct:function(e,t,r){var o=[null];o.push.apply(o,t);var n=new(Function.bind.apply(e,o));return r&&C(n,r.prototype),n}).apply(null,arguments)}function k(e){return(k=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function C(e,t){return(C=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function O(e){var t="function"==typeof Map?new Map:void 0;return(O=function(e){if(null===e||-1===Function.toString.call(e).indexOf("[native code]"))return e;if("function"!=typeof e)throw TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,r)}function r(){return F(e,arguments,k(this).constructor)}return r.prototype=Object.create(e.prototype,{constructor:{value:r,enumerable:!1,writable:!0,configurable:!0}}),C(r,e)})(e)}function E(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){}))}catch(e){}return(E=function(){return!!e})()}var j=function(e){var t;if("function"!=typeof e&&null!==e)throw TypeError("Super expression must either be null or a function");function o(){var e,t;if(!(this instanceof o))throw TypeError("Cannot call a class as a function");return e=o,t=arguments,e=k(e),function(e,t){var r;if(t&&("object"==((r=t)&&"undefined"!=typeof Symbol&&r.constructor===Symbol?"symbol":typeof r)||"function"==typeof t))return t;if(void 0===e)throw ReferenceError("this hasn't been initialised - super() hasn't been called");return e}(this,E()?Reflect.construct(e,t||[],k(this).constructor):e.apply(this,t))}return o.prototype=Object.create(e&&e.prototype,{constructor:{value:o,writable:!0,configurable:!0}}),e&&C(o,e),t=[{key:"load",value:function(){var e,t=this;return(e=function(){return function(e,t){var r,o,n,i,a={label:0,sent:function(){if(1&n[0])throw n[1];return n[1]},trys:[],ops:[]};return i={next:c(0),throw:c(1),return:c(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function c(i){return function(c){var u=[i,c];if(r)throw TypeError("Generator is already executing.");for(;a;)try{if(r=1,o&&(n=2&u[0]?o.return:u[0]?o.throw||((n=o.return)&&n.call(o),0):o.next)&&!(n=n.call(o,u[1])).done)return n;switch(o=0,n&&(u=[2&u[0],n.value]),u[0]){case 0:case 1:n=u;break;case 4:return a.label++,{value:u[1],done:!1};case 5:a.label++,o=u[1],u=[0];continue;case 7:u=a.ops.pop(),a.trys.pop();continue;default:if(!(n=(n=a.trys).length>0&&n[n.length-1])&&(6===u[0]||2===u[0])){a=0;continue}if(3===u[0]&&(!n||u[1]>n[0]&&u[1]<n[3])){a.label=u[1];break}if(6===u[0]&&a.label<n[1]){a.label=n[1],n=u;break}if(n&&a.label<n[2]){a.label=n[2],a.ops.push(u);break}n[2]&&a.ops.pop(),a.trys.pop();continue}u=t.call(e,a)}catch(e){u=[6,e],o=0}finally{r=n=0}if(5&u[0])throw u[1];return{value:u[0]?u[1]:void 0,done:!0}}}}(this,function(e){return t.app.pm.get(r()).registerType("OIDC+",{components:{SignInButton:x,AdminSettingsForm:T}}),[2]})},function(){var t=this,r=arguments;return new Promise(function(o,n){var i=e.apply(t,r);function a(e){P(i,o,n,a,c,"next",e)}function c(e){P(i,o,n,a,c,"throw",e)}a(void 0)})})()}}],function(e,t){for(var r=0;r<t.length;r++){var o=t[r];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(e,o.key,o)}}(o.prototype,t),o}(O(e.Plugin)),R=j}(),d}()});
@@ -8,16 +8,16 @@
8
8
  */
9
9
 
10
10
  module.exports = {
11
- "@ant-design/icons": "5.2.6",
12
- "@nocobase/client": "1.5.20",
13
- "antd": "5.12.8",
11
+ "@ant-design/icons": "5.6.1",
12
+ "@nocobase/client": "1.8.23",
13
+ "antd": "5.24.2",
14
14
  "react": "18.2.0",
15
- "react-router-dom": "6.21.0",
16
- "@nocobase/plugin-auth": "1.5.20",
17
- "@formily/antd-v5": "1.1.9",
15
+ "react-router-dom": "6.28.1",
16
+ "@nocobase/plugin-auth": "1.8.23",
17
+ "@formily/antd-v5": "1.2.3",
18
18
  "@formily/react": "2.3.0",
19
- "@nocobase/auth": "1.5.20",
20
- "@nocobase/server": "1.5.20",
19
+ "@nocobase/auth": "1.8.23",
20
+ "@nocobase/server": "1.8.23",
21
21
  "react-i18next": "11.18.6",
22
- "@nocobase/actions": "1.5.20"
22
+ "@nocobase/actions": "1.8.23"
23
23
  };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@typekcz-nocobase-plugins/plugin-oidc-plus",
3
3
  "displayName": "Auth: OIDC Plus",
4
4
  "description": "OIDC (OpenID Connect) authentication with extra features.",
5
- "version": "1.0.3",
5
+ "version": "1.0.4",
6
6
  "license": "AGPL-3.0",
7
7
  "main": "dist/server/index.js",
8
8
  "devDependencies": {
@@ -0,0 +1,70 @@
1
+ import { LoginOutlined } from '@ant-design/icons';
2
+ import { css, useAPIClient } from '@nocobase/client';
3
+ import { Button, Space, message } from 'antd';
4
+ import React, { useEffect } from 'react';
5
+ import { useOidcTranslation } from './locale';
6
+ import { useLocation } from 'react-router-dom';
7
+ import { Authenticator } from '@nocobase/plugin-auth/client';
8
+ import Cookies from 'js-cookie';
9
+ import { logoutCookieName } from '../constants';
10
+
11
+ export interface OIDCProvider {
12
+ clientId: string;
13
+ title: string;
14
+ }
15
+
16
+ export const OIDCButton = ({ authenticator }: { authenticator: Authenticator }) => {
17
+ const { t } = useOidcTranslation();
18
+ const api = useAPIClient();
19
+ const location = useLocation();
20
+ const params = new URLSearchParams(location.search);
21
+ const redirect = params.get('redirect');
22
+
23
+ const login = async () => {
24
+ const response = await api.request({
25
+ method: 'post',
26
+ url: 'oidc:getAuthUrl',
27
+ headers: {
28
+ 'X-Authenticator': authenticator.name,
29
+ },
30
+ data: {
31
+ redirect,
32
+ },
33
+ });
34
+
35
+ const authUrl = response?.data?.data;
36
+ window.location.replace(authUrl);
37
+ };
38
+
39
+ useEffect(() => {
40
+ const logoutUrl = Cookies.get(logoutCookieName);
41
+ if (logoutUrl) {
42
+ const logoutUrlObj = new URL(logoutUrl);
43
+ logoutUrlObj.searchParams.set('post_logout_redirect_uri', window.location.href);
44
+ Cookies.remove(logoutCookieName, { domain: window.location.hostname });
45
+ window.location.href = logoutUrlObj.href;
46
+ }
47
+ const name = params.get('authenticator');
48
+ const error = params.get('error');
49
+ if (name !== authenticator.name) {
50
+ return;
51
+ }
52
+ if (error) {
53
+ message.error(t(error));
54
+ return;
55
+ }
56
+ });
57
+
58
+ return (
59
+ <Space
60
+ direction="vertical"
61
+ className={css`
62
+ display: flex;
63
+ `}
64
+ >
65
+ <Button shape="round" block icon={<LoginOutlined />} onClick={login}>
66
+ {t(authenticator.title)}
67
+ </Button>
68
+ </Space>
69
+ );
70
+ };
@@ -0,0 +1,359 @@
1
+ import { CopyOutlined } from '@ant-design/icons';
2
+ import { ArrayItems, FormTab } from '@formily/antd-v5';
3
+ import { observer } from '@formily/react';
4
+ import { FormItem, Input, SchemaComponent, useApp } from '@nocobase/client';
5
+ import { Card, Space, message } from 'antd';
6
+ import React, { useMemo } from 'react';
7
+ import { lang, useOidcTranslation } from './locale';
8
+
9
+ const schema = {
10
+ type: 'object',
11
+ properties: {
12
+ public: {
13
+ type: 'object',
14
+ properties: {
15
+ autoSignup: {
16
+ 'x-decorator': 'FormItem',
17
+ type: 'boolean',
18
+ title: '{{t("Sign up automatically when the user does not exist")}}',
19
+ 'x-component': 'Checkbox',
20
+ default: true,
21
+ },
22
+ },
23
+ },
24
+ oidc: {
25
+ type: 'object',
26
+ properties: {
27
+ collapse: {
28
+ type: 'void',
29
+ 'x-component': 'FormTab',
30
+ properties: {
31
+ basic: {
32
+ type: 'void',
33
+ 'x-component': 'FormTab.TabPane',
34
+ 'x-component-props': {
35
+ tab: lang('Basic configuration'),
36
+ },
37
+ properties: {
38
+ issuer: {
39
+ type: 'string',
40
+ title: '{{t("Issuer")}}',
41
+ 'x-component': 'Input',
42
+ 'x-decorator': 'FormItem',
43
+ required: true,
44
+ },
45
+ clientId: {
46
+ type: 'string',
47
+ title: '{{t("Client ID")}}',
48
+ 'x-component': 'Input',
49
+ 'x-decorator': 'FormItem',
50
+ required: true,
51
+ },
52
+ clientSecret: {
53
+ type: 'string',
54
+ title: '{{t("Client Secret")}}',
55
+ 'x-component': 'Input',
56
+ 'x-decorator': 'FormItem',
57
+ required: true,
58
+ },
59
+ scope: {
60
+ type: 'string',
61
+ title: '{{t("scope")}}',
62
+ 'x-component': 'Input',
63
+ 'x-decorator': 'FormItem',
64
+ 'x-decorator-props': {
65
+ tooltip: '{{t("Default: openid profile email")}}',
66
+ },
67
+ },
68
+ idTokenSignedResponseAlg: {
69
+ type: 'string',
70
+ title: '{{t("id_token signed response algorithm")}}',
71
+ 'x-component': 'Select',
72
+ 'x-decorator': 'FormItem',
73
+ enum: [
74
+ { label: 'HS256', value: 'HS256' },
75
+ { label: 'HS384', value: 'HS384' },
76
+ { label: 'HS512', value: 'HS512' },
77
+ { label: 'RS256', value: 'RS256' },
78
+ { label: 'RS384', value: 'RS384' },
79
+ { label: 'RS512', value: 'RS512' },
80
+ { label: 'ES256', value: 'ES256' },
81
+ { label: 'ES384', value: 'ES384' },
82
+ { label: 'ES512', value: 'ES512' },
83
+ { label: 'PS256', value: 'PS256' },
84
+ { label: 'PS384', value: 'PS384' },
85
+ { label: 'PS512', value: 'PS512' },
86
+ ],
87
+ },
88
+ },
89
+ },
90
+ mapping: {
91
+ type: 'void',
92
+ 'x-component': 'FormTab.TabPane',
93
+ 'x-component-props': {
94
+ tab: lang('Field mapping'),
95
+ },
96
+ properties: {
97
+ fieldMap: {
98
+ title: '{{t("Field Map")}}',
99
+ type: 'array',
100
+ 'x-decorator': 'FormItem',
101
+ 'x-component': 'ArrayItems',
102
+ items: {
103
+ type: 'object',
104
+ 'x-decorator': 'ArrayItems.Item',
105
+ properties: {
106
+ space: {
107
+ type: 'void',
108
+ 'x-component': 'Space',
109
+ properties: {
110
+ source: {
111
+ type: 'string',
112
+ 'x-decorator': 'FormItem',
113
+ 'x-component': 'Input',
114
+ 'x-component-props': {
115
+ placeholder: '{{t("source")}}',
116
+ },
117
+ },
118
+ target: {
119
+ type: 'string',
120
+ 'x-decorator': 'FormItem',
121
+ 'x-component': 'Input',
122
+ 'x-component-props': {
123
+ placeholder: '{{t("target")}}',
124
+ },
125
+ },
126
+ remove: {
127
+ type: 'void',
128
+ 'x-decorator': 'FormItem',
129
+ 'x-component': 'ArrayItems.Remove',
130
+ },
131
+ },
132
+ },
133
+ },
134
+ },
135
+ properties: {
136
+ add: {
137
+ type: 'void',
138
+ title: 'Add',
139
+ 'x-component': 'ArrayItems.Addition',
140
+ },
141
+ },
142
+ },
143
+ userBindField: {
144
+ type: 'string',
145
+ title: '{{t("Use this field to bind the user")}}',
146
+ 'x-component': 'Select',
147
+ 'x-decorator': 'FormItem',
148
+ default: 'email',
149
+ enum: [
150
+ { label: lang('Email'), value: 'email' },
151
+ { label: lang('Username'), value: 'username' },
152
+ ],
153
+ required: true,
154
+ },
155
+ },
156
+ },
157
+ advanced: {
158
+ type: 'void',
159
+ 'x-component': 'FormTab.TabPane',
160
+ 'x-component-props': {
161
+ tab: lang('Advanced configuration'),
162
+ },
163
+ properties: {
164
+ logout: {
165
+ type: 'boolean',
166
+ title: '{{t("RP-initiated logout")}}',
167
+ 'x-component': 'Checkbox',
168
+ 'x-decorator': 'FormItem',
169
+ 'x-decorator-props': {
170
+ tooltip:
171
+ '{{t("Performs logout on the issuer (uses end_session_endpoint in the issuer configuration)")}}',
172
+ },
173
+ },
174
+ http: {
175
+ type: 'boolean',
176
+ title: '{{t("HTTP")}}',
177
+ 'x-component': 'Checkbox',
178
+ 'x-decorator': 'FormItem',
179
+ 'x-decorator-props': {
180
+ tooltip: '{{t("Check if NocoBase is running on HTTP protocol")}}',
181
+ },
182
+ },
183
+ port: {
184
+ type: 'number',
185
+ title: '{{t("Port")}}',
186
+ 'x-component': 'InputNumber',
187
+ 'x-decorator': 'FormItem',
188
+ 'x-decorator-props': {
189
+ tooltip: '{{t("The port number of the NocoBase service if it is not 80 or 443")}}',
190
+ },
191
+ 'x-component-props': {
192
+ style: {
193
+ width: '15%',
194
+ minWidth: '100px',
195
+ },
196
+ },
197
+ },
198
+ stateToken: {
199
+ type: 'string',
200
+ title: '{{t("State token")}}',
201
+ 'x-component': 'Input',
202
+ 'x-decorator': 'FormItem',
203
+ description: lang(
204
+ "The state token helps prevent CSRF attacks. It's recommended to leave it blank for automatic random generation.",
205
+ ),
206
+ },
207
+ exchangeBodyKeys: {
208
+ type: 'array',
209
+ title: '{{t("Pass parameters in the authorization code grant exchange")}}',
210
+ 'x-decorator': 'FormItem',
211
+ 'x-component': 'ArrayItems',
212
+ default: [
213
+ { paramName: '', optionsKey: 'clientId' },
214
+ {
215
+ paramName: '',
216
+ optionsKey: 'clientSecret',
217
+ },
218
+ ],
219
+ items: {
220
+ type: 'object',
221
+ 'x-decorator': 'ArrayItems.Item',
222
+ properties: {
223
+ space: {
224
+ type: 'void',
225
+ 'x-component': 'Space',
226
+ properties: {
227
+ enabled: {
228
+ type: 'boolean',
229
+ 'x-decorator': 'FormItem',
230
+ 'x-component': 'Checkbox',
231
+ },
232
+ optionsKey: {
233
+ type: 'string',
234
+ 'x-decorator': 'FormItem',
235
+ 'x-decorator-props': {
236
+ style: {
237
+ width: '100px',
238
+ },
239
+ },
240
+ 'x-component': 'Select',
241
+ 'x-read-pretty': true,
242
+ enum: [
243
+ { label: lang('Client ID'), value: 'clientId' },
244
+ { label: lang('Client Secret'), value: 'clientSecret' },
245
+ ],
246
+ },
247
+ paramName: {
248
+ type: 'string',
249
+ 'x-decorator': 'FormItem',
250
+ 'x-component': 'Input',
251
+ 'x-component-props': {
252
+ placeholder: '{{t("Parameter name")}}',
253
+ },
254
+ },
255
+ },
256
+ },
257
+ },
258
+ },
259
+ },
260
+ userInfoMethod: {
261
+ type: 'string',
262
+ title: '{{t("Method to call the user info endpoint")}}',
263
+ 'x- decorator': 'FormItem',
264
+ 'x-component': 'Radio.Group',
265
+ default: 'GET',
266
+ enum: [
267
+ {
268
+ label: 'GET',
269
+ value: 'GET',
270
+ },
271
+ {
272
+ label: 'POST',
273
+ value: 'POST',
274
+ },
275
+ ],
276
+ 'x-reactions': [
277
+ {
278
+ dependencies: ['.accessTokenVia'],
279
+ when: '{{$deps[0] === "query"}}',
280
+ fulfill: {
281
+ state: {
282
+ value: 'GET',
283
+ },
284
+ },
285
+ },
286
+ {
287
+ dependencies: ['.accessTokenVia'],
288
+ when: '{{$deps[0] === "body"}}',
289
+ fulfill: {
290
+ state: {
291
+ value: 'POST',
292
+ },
293
+ },
294
+ },
295
+ ],
296
+ },
297
+ accessTokenVia: {
298
+ type: 'string',
299
+ title: '{{t("Where to put the access token when calling the user info endpoint")}}',
300
+ 'x- decorator': 'FormItem',
301
+ 'x-component': 'Radio.Group',
302
+ default: 'header',
303
+ enum: [
304
+ {
305
+ label: lang('Header'),
306
+ value: 'header',
307
+ },
308
+ {
309
+ label: lang('Body (Use with POST method)'),
310
+ value: 'body',
311
+ },
312
+ {
313
+ label: lang('Query parameters (Use with GET method)'),
314
+ value: 'query',
315
+ },
316
+ ],
317
+ },
318
+ },
319
+ },
320
+ },
321
+ },
322
+ },
323
+ },
324
+ usage: {
325
+ type: 'void',
326
+ 'x-component': 'Usage',
327
+ },
328
+ },
329
+ };
330
+
331
+ const Usage = observer(
332
+ () => {
333
+ const { t } = useOidcTranslation();
334
+ const app = useApp();
335
+
336
+ const url = useMemo(() => {
337
+ return app.getApiUrl('oidc:redirect');
338
+ }, [app]);
339
+
340
+ const copy = (text: string) => {
341
+ navigator.clipboard.writeText(text);
342
+ message.success(t('Copied'));
343
+ };
344
+
345
+ return (
346
+ <Card title={t('Usage')} type="inner">
347
+ <FormItem label={t('Redirect URL')}>
348
+ <Input value={url} disabled={true} addonBefore={<CopyOutlined onClick={() => copy(url)} />} />
349
+ </FormItem>
350
+ </Card>
351
+ );
352
+ },
353
+ { displayName: 'Usage' },
354
+ );
355
+
356
+ export const Options = () => {
357
+ const { t } = useOidcTranslation();
358
+ return <SchemaComponent scope={{ t }} components={{ Usage, ArrayItems, Space, FormTab }} schema={schema} />;
359
+ };
@@ -0,0 +1,19 @@
1
+ import { Plugin } from '@nocobase/client';
2
+ import AuthPlugin from '@nocobase/plugin-auth/client';
3
+ import { authType } from '../constants';
4
+ import { OIDCButton } from './OIDCButton';
5
+ import { Options } from './Options';
6
+
7
+ export class PluginOIDCClient extends Plugin {
8
+ async load() {
9
+ const auth = this.app.pm.get(AuthPlugin);
10
+ auth.registerType(authType, {
11
+ components: {
12
+ SignInButton: OIDCButton,
13
+ AdminSettingsForm: Options,
14
+ },
15
+ });
16
+ }
17
+ }
18
+
19
+ export default PluginOIDCClient;
@@ -0,0 +1,18 @@
1
+ import { i18n } from '@nocobase/client';
2
+ import { useTranslation } from 'react-i18next';
3
+
4
+ export const NAMESPACE = 'oidc';
5
+
6
+ // i18n.addResources('zh-CN', NAMESPACE, zhCN);
7
+ // i18n.addResources('en-US', NAMESPACE, enUS);
8
+ // i18n.addResources('ja-JP', NAMESPACE, jaJP);
9
+ // i18n.addResources('ru-RU', NAMESPACE, ruRU);
10
+ // i18n.addResources('tr-TR', NAMESPACE, trTR);
11
+
12
+ export function lang(key: string) {
13
+ return i18n.t(key, { ns: NAMESPACE });
14
+ }
15
+
16
+ export function useOidcTranslation() {
17
+ return useTranslation(NAMESPACE);
18
+ }
@@ -0,0 +1,7 @@
1
+ // @ts-ignore
2
+ import { name } from '../package.json';
3
+
4
+ export const authType = 'OIDC+';
5
+ export const cookieName = 'tnp_oidc_plus';
6
+ export const logoutCookieName = 'tnp_oidc_plus_logout';
7
+ export const namespace = name;
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './server';
2
+ export { default } from './server';
@@ -0,0 +1,40 @@
1
+ {
2
+ "Enable": "Enable",
3
+ "Issuer": "Issuer",
4
+ "OIDC manager": "OIDC manager",
5
+ "OIDC Providers": "OIDC Providers",
6
+ "Provider name": "Name",
7
+ "Client id": "Client id",
8
+ "Client secret": "Client secret",
9
+ "Openid configuration": "Openid configuration",
10
+ "Authorization endpoint": "Authorization endpoint",
11
+ "Access token endpoint": "Access token endpoint",
12
+ "JWKS endpoint": "JWKS endpoint",
13
+ "Userinfo endpoint": "Userinfo endpoint",
14
+ "Redirect url": "Redirect url",
15
+ "Logout endpoint": "Logout endpoint",
16
+ "Id token sign alg": "Id token sign alg",
17
+ "Add provider": "Add",
18
+ "Edit provider": "Edit",
19
+ "Delete provider": "Delete",
20
+ "Sign in button name, which will be displayed on the sign in page": "Sign in button name, which will be displayed on the sign in page",
21
+ "Use this field to bind the user": "Use this field to bind the user",
22
+ "Sign up automatically when the user does not exist": "Sign up automatically when the user does not exist",
23
+ "Username must be 2-16 characters in length (excluding @.<>\"'/)": "Username must be 2-16 characters in length (excluding @.<>\"'/)",
24
+ "User not found": "User not found",
25
+ "Basic configuration": "Basic configuration",
26
+ "Field mapping": "Field mapping",
27
+ "Advanced configuration": "Advanced configuration",
28
+ "Usage": "Usage",
29
+ "Redirect URL": "Redirect URL",
30
+ "Check if NocoBase is running on HTTP protocol": "Check if NocoBase is running on HTTP protocol",
31
+ "The port number of the NocoBase service if it is not 80 or 443": "The port number of the NocoBase service if it is not 80 or 443",
32
+ "Pass parameters in the authorization code grant exchange": "Pass parameters in the authorization code grant exchange",
33
+ "Method to call the user info endpoint": "Method to call the user info endpoint",
34
+ "Where to put the access token when calling the user info endpoint": "Where to put the access token when calling the user info endpoint",
35
+ "Header": "Header",
36
+ "Body (Use with POST method)": "Body (Use with POST method)",
37
+ "Query parameters (Use with GET method)": "Query parameters (Use with GET method)",
38
+ "Parameter name": "Parameter name",
39
+ "The state token helps prevent CSRF attacks. It's recommended to leave it blank for automatic random generation.": "The state token helps prevent CSRF attacks. It's recommended to leave it blank for automatic random generation."
40
+ }