{"id":2964,"date":"2017-09-06T07:17:01","date_gmt":"2017-09-06T07:17:01","guid":{"rendered":"https:\/\/intelligentbee.com\/blog\/?p=2964"},"modified":"2025-03-06T07:28:30","modified_gmt":"2025-03-06T07:28:30","slug":"symfony-oauth-authentication-mobile-application","status":"publish","type":"post","link":"https:\/\/intelligentbee.com\/blog\/symfony-oauth-authentication-mobile-application\/","title":{"rendered":"Symfony OAuth Authentication for Your Mobile Application"},"content":{"rendered":"<p class=\"graf graf--p\">Let\u2019s say you built an API using Symfony and you need to access it from a mobile application using authenticated requests on behalf of your users.<\/p>\n<p class=\"graf graf--p\">Here\u2019s how to make this work using Symfony 2.8 and Doctrine.<\/p>\n<h2 class=\"graf graf--h3\">Install FOSOAuthServerBundle<\/h2>\n<p class=\"graf graf--p\">We will use the <a class=\"markup--anchor markup--p-anchor\" href=\"https:\/\/github.com\/FriendsOfSymfony\/FOSOAuthServerBundle\" target=\"_blank\" rel=\"noopener\" data-href=\"https:\/\/github.com\/FriendsOfSymfony\/FOSOAuthServerBundle\">FOSOAuthServerBundle<\/a> to implement this feature. Install it using the following command:<\/p>\n<pre class=\"graf graf--pre\">composer require friendsofsymfony\/oauth-server-bundle<\/pre>\n<p class=\"graf graf--p\">Next, enable the bundle in the <code class=\"markup--code markup--p-code\">AppKernel.php<\/code> file:<\/p>\n<pre class=\"lang:default decode:true graf graf--pre\">public function registerBundles()\r\n{\r\n    $bundles = array(\r\n        \/\/ ...\r\n        new FOS\\OAuthServerBundle\\FOSOAuthServerBundle(),\r\n    );\r\n}<\/pre>\n<h4 class=\"graf graf--h4\">Create OAuth model\u00a0classes<\/h4>\n<p class=\"graf graf--p\">To create the OAuth model classes just add the following files to your project. Here we already have FOSUserBundle installed and set up to use the <code class=\"markup--code markup--p-code\">ApiBundle\\Entity\\User<\/code> class.<\/p>\n<p class=\"graf graf--p\"><strong class=\"markup--strong markup--p-strong\">src\/ApiBundle\/Entity\/Client.php<\/strong><\/p>\n<pre class=\"lang:default decode:true graf graf--pre\">&lt;?php\r\nnamespace ApiBundle\\Entity;\r\n\r\nuse FOS\\OAuthServerBundle\\Entity\\Client as BaseClient;\r\nuse Doctrine\\ORM\\Mapping as ORM;\r\n\r\n\/**\r\n * @ORM\\Entity\r\n *\/\r\nclass Client extends BaseClient\r\n{\r\n    \/**\r\n     * @ORM\\Id\r\n     * @ORM\\Column(type=\"integer\")\r\n     * @ORM\\GeneratedValue(strategy=\"AUTO\")\r\n     *\/\r\n    protected $id;\r\n\r\n    public function __construct()\r\n    {\r\n        parent::__construct();\r\n        \/\/ your own logic\r\n    }\r\n}<\/pre>\n<p class=\"graf graf--p\"><strong class=\"markup--strong markup--p-strong\">src\/ApiBundle\/Entity\/AccessToken.php<\/strong><\/p>\n<pre class=\"lang:default decode:true graf graf--pre \">&lt;?php\r\nnamespace ApiBundle\\Entity;\r\n\r\nuse FOS\\OAuthServerBundle\\Entity\\AccessToken as BaseAccessToken;\r\nuse Doctrine\\ORM\\Mapping as ORM;\r\n\r\n\/**\r\n * @ORM\\Entity\r\n *\/\r\nclass AccessToken extends BaseAccessToken\r\n{\r\n    \/**\r\n     * @ORM\\Id\r\n     * @ORM\\Column(type=\"integer\")\r\n     * @ORM\\GeneratedValue(strategy=\"AUTO\")\r\n     *\/\r\n    protected $id;\r\n\r\n    \/**\r\n     * @ORM\\ManyToOne(targetEntity=\"Client\")\r\n     * @ORM\\JoinColumn(nullable=false)\r\n     *\/\r\n    protected $client;\r\n\r\n    \/**\r\n     * @ORM\\ManyToOne(targetEntity=\"User\")\r\n     *\/\r\n    protected $user;\r\n}\r\n<\/pre>\n<p class=\"graf graf--p\"><strong class=\"markup--strong markup--p-strong\">src\/ApiBundle\/Entity\/RefreshToken.php<\/strong><\/p>\n<pre class=\"lang:default decode:true graf graf--pre \">&lt;?php\r\nnamespace ApiBundle\\Entity;\r\n\r\nuse FOS\\OAuthServerBundle\\Entity\\RefreshToken as BaseRefreshToken;\r\nuse Doctrine\\ORM\\Mapping as ORM;\r\n\r\n\/**\r\n * @ORM\\Entity\r\n *\/\r\nclass RefreshToken extends BaseRefreshToken\r\n{\r\n    \/**\r\n     * @ORM\\Id\r\n     * @ORM\\Column(type=\"integer\")\r\n     * @ORM\\GeneratedValue(strategy=\"AUTO\")\r\n     *\/\r\n    protected $id;\r\n\r\n    \/**\r\n     * @ORM\\ManyToOne(targetEntity=\"Client\")\r\n     * @ORM\\JoinColumn(nullable=false)\r\n     *\/\r\n    protected $client;\r\n\r\n    \/**\r\n     * @ORM\\ManyToOne(targetEntity=\"User\")\r\n     *\/\r\n    protected $user;\r\n}\r\n<\/pre>\n<p class=\"graf graf--p\"><strong class=\"markup--strong markup--p-strong\">src\/ApiBundle\/Entity\/AuthCode.php<\/strong><\/p>\n<pre class=\"lang:default decode:true graf graf--pre \">&lt;?php\r\nnamespace ApiBundle\\Entity;\r\n\r\nuse FOS\\OAuthServerBundle\\Entity\\AuthCode as BaseAuthCode;\r\nuse Doctrine\\ORM\\Mapping as ORM;\r\n\r\n\/**\r\n * @ORM\\Entity\r\n *\/\r\nclass AuthCode extends BaseAuthCode\r\n{\r\n    \/**\r\n     * @ORM\\Id\r\n     * @ORM\\Column(type=\"integer\")\r\n     * @ORM\\GeneratedValue(strategy=\"AUTO\")\r\n     *\/\r\n    protected $id;\r\n\r\n    \/**\r\n     * @ORM\\ManyToOne(targetEntity=\"Client\")\r\n     * @ORM\\JoinColumn(nullable=false)\r\n     *\/\r\n    protected $client;\r\n\r\n    \/**\r\n     * @ORM\\ManyToOne(targetEntity=\"User\")\r\n     *\/\r\n    protected $user;\r\n}\r\n<\/pre>\n<h4 class=\"graf graf--h4\">Configure FOSOAuthServerBundle<\/h4>\n<p class=\"graf graf--p\">Import the routing configuration in your <code class=\"markup--code markup--p-code\">app\/config\/routing.yml<\/code> file:<\/p>\n<pre class=\"lang:default decode:true graf graf--pre \">fos_oauth_server_token:\r\n    resource: \"@FOSOAuthServerBundle\/Resources\/config\/routing\/token.xml\"\r\nfos_oauth_server_authorize:\r\n    resource: \"@FOSOAuthServerBundle\/Resources\/config\/routing\/authorize.xml\"<\/pre>\n<p class=\"graf graf--p\">Add FOSOAuthServerBundle settings in <code class=\"markup--code markup--p-code\">app\/config\/config.yml<\/code>:<\/p>\n<pre class=\"graf graf--pre\">fos_oauth_server:\r\n    db_driver: orm       # Drivers available: orm, mongodb, or propel\r\n    client_class:        ApiBundle\\Entity\\Client\r\n    access_token_class:  ApiBundle\\Entity\\AccessToken\r\n    refresh_token_class: ApiBundle\\Entity\\RefreshToken\r\n    auth_code_class:     ApiBundle\\Entity\\AuthCode\r\n    service:\r\n        user_provider: fos_user.user_provider.username<\/pre>\n<h4 class=\"graf graf--h4\">Back to the\u00a0models<\/h4>\n<p class=\"graf graf--p\">Generate a migration and migrate the database:<\/p>\n<pre class=\"graf graf--pre\">php app\/console doctrine:migrations:diff\r\nphp app\/console doctrine:migrations:migrate<\/pre>\n<p class=\"graf graf--p\">\u2026or, if you\u2019re not using migrations, just update the database schema:<\/p>\n<pre class=\"graf graf--pre\">php app\/console doctrine:schema:update --force<\/pre>\n<h4 class=\"graf graf--h4\">Configure your application\u2019s <code class=\"markup--code markup--h4-code\">security<\/code><\/h4>\n<p class=\"graf graf--p\">Edit your <code class=\"markup--code markup--p-code\">app\/config\/security.yml<\/code> file to add FOSOAuthServerBundle specific configuration:<\/p>\n<pre class=\"lang:default decode:true graf graf--pre \"># ...\r\n\r\n    firewalls:\r\n        oauth_token: # Everyone can access the access token URL.\r\n            pattern: ^\/oauth\/v2\/token\r\n            security: false\r\n            \r\n        api:\r\n            pattern:    ^\/api\r\n            fos_oauth:  true\r\n            stateless:  true\r\n            anonymous:  true # can be omitted as its default value\r\n    \r\n    # ...\r\n\r\n    access_control:\r\n        - { path: ^\/api, role: IS_AUTHENTICATED_FULLY }<\/pre>\n<h4 class=\"graf graf--h4\">Create a\u00a0client<\/h4>\n<p class=\"graf graf--p\">Before you can generate tokens, you need to create a <code class=\"markup--code markup--p-code\">Client<\/code> using the <code class=\"markup--code markup--p-code\">ClientManager<\/code>. For this, create a new Symfony command:<\/p>\n<pre class=\"lang:default decode:true graf graf--pre \">&lt;?php\r\nnamespace ApiBundle\\Command;\r\n\r\nuse Symfony\\Bundle\\FrameworkBundle\\Command\\ContainerAwareCommand;\r\nuse Symfony\\Component\\Console\\Input\\InputInterface;\r\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\r\n\r\nclass OAuthAddClientCommand extends ContainerAwareCommand\r\n{\r\n    protected function configure()\r\n    {\r\n        $this\r\n            -&gt;setName('oauth:add-client')\r\n            -&gt;setDescription(\"Ads a new client for OAuth\")\r\n        ;\r\n    }\r\n\r\n    protected function execute(InputInterface $input, OutputInterface $output)\r\n    {\r\n        $redirectUri = $this-&gt;getContainer()-&gt;getParameter('router.request_context.scheme') . \":\/\/\" . $this-&gt;getContainer()-&gt;getParameter('router.request_context.host');\r\n        $clientManager = $this-&gt;getContainer()-&gt;get('fos_oauth_server.client_manager.default');\r\n        $client = $clientManager-&gt;createClient();\r\n        $client-&gt;setRedirectUris(array($redirectUri));\r\n        $client-&gt;setAllowedGrantTypes(array('refresh_token', 'password'));\r\n        $clientManager-&gt;updateClient($client);\r\n    }\r\n}\r\n<\/pre>\n<p class=\"graf graf--p\">Now run the above command to generate your first OAuth client:<\/p>\n<pre class=\"graf graf--pre\">php app\/console oauth:add-client<\/pre>\n<p class=\"graf graf--p\">This client will be able to generate tokens and refresh tokens using the user\u2019s username and password. You can find it\u2019s data in the database <code class=\"markup--code markup--p-code\">client<\/code> table. The <code class=\"markup--code markup--p-code\">token<\/code> endpoint is at <code class=\"markup--code markup--p-code\">\/oauth\/v2\/token<\/code> by default.<\/p>\n<h4 class=\"graf graf--h4\">Document using NelmioApiDocBundle<\/h4>\n<p class=\"graf graf--p\">If you use the NelmioApiDocBundle to document your API, you can add these OAuth methods too. Create a new YAML file in <code class=\"markup--code markup--p-code\">src\/ApiBundle\/Resources\/apidoc\/oauth.yml<\/code>:<\/p>\n<pre class=\"graf graf--pre\">grant_type_password:\r\n    requirements: []\r\n    views: []\r\n    filters: []\r\n    parameters:\r\n        grant_type:\r\n            dataType: string\r\n            required: true\r\n            name: grant_type\r\n            description: Grant Type (password)\r\n            readonly: false\r\n        client_id:\r\n            dataType: string\r\n            required: true\r\n            name: client_id\r\n            description: Client Id\r\n            readonly: false\r\n        client_secret:\r\n            dataType: string\r\n            required: true\r\n            name: client_secret\r\n            description: client Secret\r\n            readonly: false\r\n        username:\r\n            dataType: string\r\n            required: true\r\n            name: username\r\n            description: Username\r\n            readonly: false\r\n        password:\r\n            dataType: string\r\n            required: true\r\n            name: password\r\n            description: Password\r\n            readonly: false\r\n    input: null\r\n    output: null\r\n    link: null\r\n    description: \"Get OAuth token for user using username and password\"\r\n    section: \"OAuth\"\r\n    documentation: null\r\n    resource: null\r\n    method: \"POST\"\r\n    host: \"\"\r\n    uri: \"\/oauth\/v2\/token\"\r\n    response:\r\n        token:\r\n            dataType: string\r\n            required: true\r\n            description: OAuth token\r\n            readonly: true\r\n    route:\r\n        path: \/oauth\/v2\/token\r\n        defaults:\r\n            _controller: FOS\\UserBundle\\Controller\\SecurityController::checkAction\r\n        requirements: []\r\n        options:\r\n            compiler_class: Symfony\\Component\\Routing\\RouteCompiler\r\n        host: ''\r\n        schemes: []\r\n        methods: [ 'POST' ]\r\n        condition: ''\r\n    https: false\r\n    authentication: false\r\n    authenticationRoles: []\r\n    cache: null\r\n    deprecated: false\r\n    statusCodes: []\r\n    resourceDescription: null\r\n    responseMap: []\r\n    parsedResponseMap: []\r\n    tags: []\r\n    \r\ngrant_type_refresh_token:\r\n    requirements: []\r\n    views: []\r\n    filters: []\r\n    parameters:\r\n        grant_type:\r\n            dataType: string\r\n            required: true\r\n            name: grant_type\r\n            description: Grant Type (refresh_token)\r\n            readonly: false\r\n        client_id:\r\n            dataType: string\r\n            required: true\r\n            name: client_id\r\n            description: Client Id\r\n            readonly: false\r\n        client_secret:\r\n            dataType: string\r\n            required: true\r\n            name: client_secret\r\n            description: client Secret\r\n            readonly: false\r\n        refresh_token:\r\n            dataType: string\r\n            required: true\r\n            name: refresh_token\r\n            description: Refresh token\r\n            readonly: false\r\n    input: null\r\n    output: null\r\n    link: null\r\n    description: \"Get new OAuth token using refresh token\"\r\n    section: \"OAuth\"\r\n    documentation: null\r\n    resource: null\r\n    method: \"POST\"\r\n    host: \"\"\r\n    uri: \"\/oauth\/v2\/token\"\r\n    response:\r\n        token:\r\n            dataType: string\r\n            required: true\r\n            description: OAuth token\r\n            readonly: true\r\n    route:\r\n        path: \/oauth\/v2\/token\r\n        defaults:\r\n            _controller: FOS\\UserBundle\\Controller\\SecurityController::checkAction\r\n        requirements: []\r\n        options:\r\n            compiler_class: Symfony\\Component\\Routing\\RouteCompiler\r\n        host: ''\r\n        schemes: []\r\n        methods: [ 'POST' ]\r\n        condition: ''\r\n    https: false\r\n    authentication: false\r\n    authenticationRoles: []\r\n    cache: null\r\n    deprecated: false\r\n    statusCodes: []\r\n    resourceDescription: null\r\n    responseMap: []\r\n    parsedResponseMap: []\r\n    tags: []<\/pre>\n<p class=\"graf graf--p\">Add a new <code class=\"markup--code markup--p-code\">NelmioApiYmlProvider.php<\/code> file in <code class=\"markup--code markup--p-code\">src\/ApiBundle\/Service<\/code> folder:<\/p>\n<pre class=\"lang:default decode:true graf graf--pre \">&lt;?php\r\n\r\nnamespace ApiBundle\\Service;\r\n\r\nuse Nelmio\\ApiDocBundle\\Annotation\\ApiDoc;\r\nuse Nelmio\\ApiDocBundle\\Extractor\\AnnotationsProviderInterface;\r\nuse Symfony\\Component\\Finder\\Finder;\r\nuse Symfony\\Component\\Routing\\Route;\r\nuse Symfony\\Component\\Yaml\\Yaml;\r\n\r\n\/**\r\n * Generate annotations for vendor routes to be displayed in Nelmio ApiDoc.\r\n *\/\r\nclass NelmioApiYmlProvider implements AnnotationsProviderInterface\r\n{\r\n    private $vendorFolder;\r\n\r\n    public function __construct($vendorFolder)\r\n    {\r\n        $this-&gt;vendorFolder = $vendorFolder;\r\n    }\r\n    \/**\r\n     * {@inheritdoc}\r\n     *\/\r\n    public function getAnnotations()\r\n    {\r\n        $annotations = [];\r\n        $configDirectories = array($this-&gt;vendorFolder);\r\n\r\n        $finder = new Finder();\r\n\r\n        $finder-&gt;files()-&gt;in($configDirectories);\r\n\r\n        if (count($finder) == 0) {\r\n            return $annotations;\r\n        }\r\n\r\n        foreach ($finder as $file_) {\r\n            $data = Yaml::parse(file_get_contents($file_));\r\n\r\n            $vendors = array_keys($data);\r\n            foreach ($vendors as $vendor) {\r\n                $apiDoc = new ApiDoc($data[$vendor]);\r\n                $route = new Route(\r\n                    $data[$vendor]['route']['path'],\r\n                    $data[$vendor]['route']['defaults'],\r\n                    $data[$vendor]['route']['requirements'],\r\n                    $data[$vendor]['route']['options'],\r\n                    $data[$vendor]['route']['host'],\r\n                    $data[$vendor]['route']['schemes'],\r\n                    $data[$vendor]['route']['methods'],\r\n                    $data[$vendor]['route']['condition']\r\n                );\r\n\r\n                $apiDoc-&gt;setRoute($route);\r\n                $apiDoc-&gt;setResponse($data[$vendor]['response']);\r\n                $annotations[] = $apiDoc;\r\n            }\r\n        }\r\n\r\n        return $annotations;\r\n    }\r\n}<\/pre>\n<p>Add a new service in <code class=\"markup--code markup--p-code\">src\/ApiBundle\/Resources\/config\/services.yml<\/code> file:<\/p>\n<pre class=\"graf graf--pre\">services:\r\n    nelmio_api_doc.yml_provider.api_yml_provider:\r\n        class: ApiBundle\\Service\\NelmioApiYmlProvider\r\n        arguments:\r\n            folder: %kernel.root_dir%\/..\/src\/ApiBundle\/Resources\/apidoc\r\n        tags:\r\n            - { name: nelmio_api_doc.extractor.annotations_provider }<\/pre>\n<p class=\"graf graf--p\">You\u2019ll find now two <code class=\"markup--code markup--p-code\">\/oauth\/v2\/token<\/code> methods with different parameters listed in the <code class=\"markup--code markup--p-code\">api\/doc<\/code> section of your project.<\/p>\n<p class=\"graf graf--p\">That\u2019s all! You can now use the generated client to authenticate your users in your mobile app using OAuth.<\/p>\n<h3 class=\"graf graf--h3\">How to use the FOSOAuth ServerBundle<\/h3>\n<p class=\"graf graf--p\">First you will need to get an access token by making a POST request to the <code class=\"markup--code markup--p-code\">\/oauth\/v2\/token<\/code> endpoint with the following parameters:<\/p>\n<pre class=\"graf graf--pre\">grant_type=password\r\nclient_id=[client's id from the database followed by '_' then the corresponding random id]\r\nclient_secret=[client's secret]\r\nusername=[user's username]\r\npassword=[users's password]<\/pre>\n<p class=\"graf graf--p\">You should get back something like this:<\/p>\n<pre class=\"graf graf--pre\">{\r\n  \"access_token\": \"ZDgxZDlkOWI2N2IyZWU2ZjlhY2VlNWQxNzM0ZDhlOWY2ZTIwOTBkNGUzZDUyOGYxOTg1ZTRjZGExOTY2YjNmNw\",\r\n  \"expires_in\": 3600,\r\n  \"token_type\": \"bearer\",\r\n  \"scope\": null,\r\n  \"refresh_token\": \"MDQ3MGIwZTk5MDkwOGM5NjhkMzk5NTUyZDJjZmYwM2YzZWViZDFhZjk0NTIyZmNjNzkyMDM0YjM4ODQ2N2VhNg\"\r\n}<\/pre>\n<p class=\"graf graf--p\">Use the access token for authenticated requests by placing it in the request header:<\/p>\n<pre class=\"graf graf--pre\">Authorization: Bearer ZDgxZDlkOWI2N2IyZWU2ZjlhY2VlNWQxNzM0ZDhlOWY2ZTIwOTBkNGUzZDUyOGYxOTg1ZTRjZGExOTY2YjNmNw<\/pre>\n<p class=\"graf graf--p\">When the access token expires, you can get a new one using the <code class=\"markup--code markup--p-code\">refresh_token<\/code> grant type at the same <code class=\"markup--code markup--p-code\">\/oauth\/v2\/token<\/code> endpoint:<\/p>\n<pre class=\"graf graf--pre\">grant_type=refresh_token\r\nclient_id=[client's id from the database followed by '_' then the corresponding random id]\r\nclient_secret=[client's secret]\r\nrefresh_token=[refresh token received earlier]<\/pre>\n<p class=\"graf graf--p\">The response should be similar to:<\/p>\n<pre class=\"graf graf--pre\">{\r\n  \"access_token\": \"MjE1NjRjNDc0ZmU4NmU3NjgzOTIyZDZlNDBiMTg5OGNhMTc0MjM5OWU3MjAxN2ZjNzAwOTk4NGQxMjE5ODVhZA\",\r\n  \"expires_in\": 3600,\r\n  \"token_type\": \"bearer\",\r\n  \"scope\": null,\r\n  \"refresh_token\": \"YzM2ZWNiMGQ5MDBmOGExNjhmNDI1YjExZTkyN2U0Mzk5ZmM4NzcwNDdhNjAzZDliMjY3YzE0ZTg5NDFlZjg3MQ\"\r\n}\r\n\r\n\r\n\r\nSymfony OAuth Authentication for Your Mobile Application\r\n\r\nWe hope you enjoy the information we offer in our blog.<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Let\u2019s say you built an API using Symfony and you need to access it from a mobile application using authenticated [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":2965,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[80,82],"tags":[],"yst_prominent_words":[1046,1147,1054,1053,1052,1051,1050,1049,1048,1047,564,1045,1044,1043,1042,1041,1039,1013,900,798],"post_mailing_queue_ids":[],"_links":{"self":[{"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/posts\/2964"}],"collection":[{"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/comments?post=2964"}],"version-history":[{"count":3,"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/posts\/2964\/revisions"}],"predecessor-version":[{"id":133374,"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/posts\/2964\/revisions\/133374"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/media\/2965"}],"wp:attachment":[{"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/media?parent=2964"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/categories?post=2964"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/tags?post=2964"},{"taxonomy":"yst_prominent_words","embeddable":true,"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/yst_prominent_words?post=2964"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}