【國立臺灣大學 109學年度畢業典禮 貴賓致詞】
Commencement Address, National Taiwan University Commencement 2021
.
巨大集團 羅祥安前執行長
Giant Group Former CEO Anthony Lo
管校長,羅副校長,各位貴賓,畢業生的家長跟朋友們,還有今天最重要的2021年臺大的畢業生,大家早安、大家好!
首先恭喜你們,今天從全世界最優秀大學之一的臺灣大學畢業了!
在過去幾年,在管校長,以「大學教改」和「國際化」的理念,率領全體師生的努力之下,臺大在全世界的能見度,和各項排名,都有顯著的進步,也讓身為校友的我們倍感光榮。
是不是我們大家一起鼓掌,給自己鼓勵一下好不好!
你們今天剛剛畢業,但是你們已經創造了臺大一項新的紀錄:第一次,沒有傳統畢業典禮的畢業生。這將是你們人生一個非常特別的回憶,也預表了你們未來將會有與眾不同的表現,和非凡的成就。
現在的世界,局勢是在東方跟西方的勢力,彼此對抗而動盪不安。而全世界的經濟活動,也因為科技的突破性創新,而受到很大的影響。不盡理想的、不盡公平的全球化,也將逐漸地變成 全球在地化。而COVID-19肆虐全球的結果,使得臺灣跟全世界,大家的生活型態、社會的結構、經濟活動的節奏都被打亂了,而必須要加以全面的整理跟重新的架構。這當然是一個危機,但是更是一個絕好的機會。所以,將你們迎面而來的,是無數的創新的機會,以及冒險卻令人興奮的全新挑戰。
因為臺灣,這是第一次跟全世界各個國家,同步的站在一個嶄新的共同起跑點上面。所以我要恭喜你們,你們躬逢其會,將要進入一個充滿希望、嶄新的時代。
我是1969年從臺大畢業的,所以我想今天就以我畢業之後的50多年,人生的一些學習和體會,來跟諸位分享,給諸位作個參考。
當我大學畢業的時候,我滿懷抱負,迫不及待地想要大展身手,我當時進入臺灣最大的貿易公司工作,但是我發現我所學的很多東西,只有一些國際貿易的實務跟英文有用,至於其他我所學習到的經營管理的很多知識,恐怕都要等到10年、20年之後,如果我有幸當上高級主管才能派上用場。
更諷刺的是,我是商學系畢業的,但是工作兩年後,我發現我真的不喜歡做商人,我也不喜歡做生意,我有興趣的是做事業。
後來,幸好我有聆聽我心裡面的微弱的聲音,決定踏進自行車業,放棄掉台北最好的貿易公司的金飯碗,而去參加在大甲一個只有38個人的小公司,來追求我的夢想。
那時候許多人認為我腦筋有問題,頭殼壞去,但現在回顧回來,我當時是做了人生一個非常好的決定。
創業的過程飽受艱辛,但是我也學到了很多寶貴的經驗:我首先學到的,是在做事之前,必須要先學做人。
人必須要誠信踏實,不能自私自利,而要利他共好。
要隨時心存感恩,尊重、關切別人。要熱愛這個世界,並且要了解到一個人的能力是有限的,而必須要去建立互信、互助的人際關係;要融入群體、要發揮團隊的精神。
在做事方面,一定要以正面積極的態度,認真努力去工作。 而且不要怕失敗,要勇於接受新的挑戰。因為如果有失敗了,那只是代表說你又更靠近成功一步了。要作中學,學中作,要虛心好奇的終身學習,開拓挑戰、追求卓越。
在這裡,我想要送給大家一個很特別的畢業禮物,就是兩個「成功的錦囊」。
第一個錦囊:不管你做什麼工作,不論你職務的高低,永遠都要站在顧客的立場,來為他們設想,還能做什麼,讓他們能夠得到更多的創新價值。
第二個錦囊:無論作什麼工作,不管你職務的高低,永遠都要站在經營者的角度來為公司設想,要做什麼,才能讓公司更健康,永續經營跟發展。
既使你們把我後面講的很多東西都忘記了,沒關係,但是這個畢業禮物你們一定要記得帶回去,因為這些「同理心」的兩個錦囊,在未來會給你們帶來意想不到的祝福!
其次我體會到,名跟利都是副產品。正產品是你成為什麼樣的人,成就什麼樣的事。當正產品是對的時候,名跟利都自然會來。
所以我鼓勵大家在找工作的時候,不要太計較開始的薪水和待遇。因為事實上,一個新人,在前面三年是很難有大的貢獻的,反過來只是公司發薪水來給你培訓而已。所以選擇一個有意義的行業,找一家你可以學習進步的好公司,這才是重要的。
第三,我學習到 知識不等於智慧。諸位都學有專精,有很多寶貴的知識,但這些知識必須要實際去做才能夠產生經驗,而累積的時候會有不同經驗的反省、學習、跟體會,尤其是從那些失敗的經 驗,才能夠漸漸地凝結成有用的智慧。
我的成長過程分成三段,開始我是一個「I」型的人,我擁有一些經營管理的基本的能力。後來因為工作的需要和我的好奇心,我漸漸地學習到技術開發、製造生產、品牌推廣、全球經營、以及行銷、服務等等寶貴的經驗,這樣漸漸地,我就形成一個「T」型的一個跨界的人才。但是到了我真正找到公司未來的使命跟方向,並且把它跟我的人生目標結合在一起,熱情的、積極去推動的時候,那時候我終於成為一個「十字架」型的一個領導者。
當公司發展得比較穩定之後,我就面臨要如何來替公司規劃願景、使命、定位,跟長期策略。在這個階段,我有一些新的體會:首先,你必須要能夠畫出一個「黃金正三角形」,正三角形是最穩定的,上面是戰略,中間是戰術,下面是戰鬥。先要有正確的戰略,才能衍伸出有效的戰術,再根據這個戰術去貫徹戰鬥的執行。換言之,戰略就是做對的事情,戰術就是對的方法,戰鬥就是用對的方法去把事情做對做好,並且不斷地去持續改善。
想要能夠思考出長期的戰略,就必須要有洞察力。
我很喜歡看地球儀,就是那種在一個架子上面,有一個可以轉動的地球模型。當你把地球轉動的時候,你看到上面每一個地方、每一個國家,它的地理位置、它的自然生態,以及你思考它的歷史的演變跟它的發展,這樣子你就能夠得出一個整體的,跟全方位的一個概念。
在思考長期戰略的時候,很不容易得出一個有突破性的一個創見,它的原因往往是因為「當局者迷」的關係。
所以當我考慮長期戰略的時候,我會把自己設想,好像從一個人造衛星上面,來觀看一個轉動的地球。以宇宙格局的高度,以全球視野的廣度,以歷史演進的長度,這樣子你就能夠以一個「旁觀者清」的姿態和角度,來正面、反面深入思考,這樣子你就可能得到一個突破性的一個解答。「世界的捷安特」跟「全球在地化」的品牌經營—這樣子的策略、願景就是這樣產生的。
另外一種洞察力,是如何推出給人們有創新價值的產品跟服務。這就要時時地站在人文跟科技的十字路口,仔細觀察它們的變化,然後活用科技的進步,為人文潛在的需求,適時地提供最佳的解決方案。捷安特的Cycling世界,就是用這種方法,不斷的推出創新價值的產品。
我們在經營上也有發展出一個特別的理念,那就是「不求第一,要作惟一」。全世界的人這麼多,但沒有兩個人的指紋是一樣的, 所以每一個人都是獨一無二、都是惟一的。可見當上帝造每一個人的時候,祂都有衪特別的計劃,而且也把成功所需要的能力跟要素,已經放在你的心中,等待你取用。所以每一個人不應該只是模仿別人,或者是按照別人的期望來生活,而應該要找出並且發揮自己的天賦,真正做自己,成為惟一,才能夠活出精采的生命。
企業也是一樣,不能隨波逐流,必須要找出什麼才是對的,什麼是合適你的,什麼才是有意義、對這個世界有貢獻的,這個才是惟一。當你精益求精,努力的想要成為惟一的時侯,你才能夠找到真正正確的目標,以及永續生存的價值。如果事事都想為第一,那麼你可能去追求很多不切實際的目標,而被你無法承受的巨大壓力所摧毀。或者,因為不擇手段,最後走上一些錯誤的道路。
在長期追求惟一的過程當中,我也得到兩個重要的體會:當你決心做一件對的事情,而且不屈不撓,勇敢堅持奮鬥的時候,全宇宙的力量都會起來幫助你。另外,把一件有意義的事情,用生命的力量把它作好,你就有可能改變這個世界!
我鼓勵你們,聆聽你們內心深處的微小的聲音,讓它引導你們,去尋找到你們的天賦,了解你們的命定。
不求第一,要作惟一。做你真正的自己,成為惟一。活出你精彩的生命,享受你幸福的人生!
很多人說,生長在臺灣的人,一輩子裡面一定要完成三項挑戰:登玉山、泳渡日月潭、還有騎自行車環島。我覺得這非常有道理。所以我建議大家在畢業以後,在去當兵、就業之前,不妨參加一個自行車環島的旅行團,用九天的時間,仔細的去體會、欣賞寶島臺灣的美好,用自行車的輪胎去親吻,這塊生你長你的美麗大地,就當作是送給你自己的一個畢業禮物吧!
那教授們跟家長們,可能會想說,啊!太可惜了,我畢業的時候沒有騎自行車環島,我現在已經五六十歲了。沒關係,你這個年紀正好來從事人生的壯遊。
我們臺大的校長-管校長,在今年的一月份,就勇敢的完成他夢寐以求的自行車環島的壯遊。我們給管爺一個掌聲好不好?
有人認為臺灣很小、沒有前途,真的是這樣嗎?世界經濟論壇每年都要把全世界的國家的實力做一個調查跟排名。讓我們來看一下臺灣的排名。全世界有235個國家,就面積來講,我們排名第137;就人口來講,第57;就經濟體來講,第21;就整體競爭力來講,排名第11;而創新力,臺灣排名第4。沒錯,臺灣的確不大,但是我們很強,我們可以很有前途!
可預見的,未來的世界將會成為美、歐、亞三區鼎力的這個新局面。就像三個圓圈,各自都以自己的利益為優先,並且彼此長期的競爭、對抗和抵制,但是同時又不得不相互的維持一些必要的連結。而在每一個圈裡面,它將持續的有很多劇烈的改變,就像一個暴風圈一樣。而這三個暴風圈連結在一起,就形成一個所謂完全風暴PERFECT STORM。
最近經濟學人雜誌,把臺灣當作封面,稱臺灣為地球上最危險的地方,但同時又是科技業最關鍵的地方。我個人倒認為,未來臺灣可能是處在一個最有利的地位,就在那三個暴風圈中間的那個颱風眼,也就是這三個圈圈彼此連結交會的中心點。這個地方看起來好像危險,其實是最安全、最有利、又是最關鍵的地方。
當三個暴風圈無可避免的,參與在一個長期的競爭、對抗、跟抵制-這樣一個零和的賽局裡面。那臺灣並不屬於任何一個圈圈,也不需要去參與這些競爭、對抗跟抵制。而可以去想說,我可以在這個關鍵的中心點,替這個世界來提供什麼樣子的創新價值的貢獻。
臺灣過去50年的努力累積了很多技術開發、製造管理、金融資本,社會資源,以及民主法治體制,這些稀有而寶貴的實力。而且如果以全世界作為舞台,那麼,能夠精通中文跟英文、能夠深切的了解東方西方的文化;有專業,又能跨界,謙卑而不自卑,自信而不自大的臺灣人,可能是全世界最優秀的人才。
未來無法被預測,但是可以被創造!30年前,有誰能夠預測到說臺灣會產生世界級的台積電跟捷安特?
台積電秉持著作惟一的理念,以全球晶圓代工的戰略,厚植實力,打造了優質高效的供應鏈,把臺灣變成一個科技島。以臺灣為核心,轉動引領全世界的半導體市場。
捷安特也秉持著作惟一的理念,以世界的捷安特,全球在地化的品牌戰略,帶領A-Team把臺灣變成全世界高級車的中心,又積極的推動Cycling的新文化,把臺灣發展成一個自行車島,以臺灣為核心,轉動引領全世界自行車的市場。
在這裡我也期許我們臺大,也能夠以作惟一這樣子的理念,轉動引領世界,變成全世界最優秀人才培養的最佳搖籃。
未來的50年,臺灣將迎來前所未有最大的機運!所以臺灣是不是應該要脫離過去單純競爭的心態,而來積極的謀求長期的未來生存之道,或者更進一步要問自己:臺灣可以為世界貢獻些什麼?臺灣不應該只是消極的「根留臺灣」,而應該更主動積極的「放眼全球、立足亞太、深根臺灣」。
臺灣過去的50年,那是一個非常艱辛跟特別的階段。很慶幸的,臺灣在那個階段裡面,是屬於勝利的這一組。
現在是應該到了必須要了解過去、深入現在、策劃未來,這個重要關鍵的時刻。要如何的以宇宙格局的高度、以全球視野的廣度、以及歷史演進的長度,衡外情,量己力,來為臺灣重新的定位,重新畫一個正確的戰略、戰術、戰鬥的黃金三角形。讓臺灣能夠真正發揮自己的長處,能夠打造創新價值的優勢,成為惟一、做Only One!要能夠創造一個更安全、更廣闊、更興盛-全新的藍海。臺灣必須要成為世界的好公民,繼續的為全世界做更多、更好的貢獻!
2021的畢業生們,在你們當中有未來國家的領導人、有全球企業的創造者、有在社會各行各業、方方面面的核心人才,所以臺灣未來50年的未來,就掌握在你們手中了。
最後,我想用一位著名科學家的名言來作結尾: Life is like riding a bicycle. To keep your balance, you must keep moving. 人生就像騎自行車,要保持你平衡最好的方法,就是繼續不斷地向前進。
你知道這是誰的名言嗎? 沒錯,就是愛因斯坦!
2021的畢業生們,請你騎上你人生的自行車,勇敢的向前邁進,去開創你獨一無二、精彩無比的新生命跟未來。再一次,恭喜你們,並且祝福你們每一位,將來都能夠有健康、幸福、精彩跟有意義的人生!
謝謝!
.
==============================
.
President Kuan, Executive Vice President Luo, Distinguished guests, family, friends, and most importantly, the graduating class of 2021, good morning!
First of all, congratulations! Today, you are graduating from one of the best universities in the world – National Taiwan University!
Over the last few years, President Kuan led the university forward with his vision of “educational reform” and internationalization”. Together with all the faculty and students, NTU has made significant progress in both ranking and global visibility, making us, alumni, extremely proud.
I think we deserve to give ourselves a round of applause!
You are the graduating class of 2021 and already, you have set a new record for NTU: For the first time, there will not be a traditional graduation ceremony. Let this be a very special memory of your life and a sign of the difference you will make in your future with extraordinary achievements.
The world is now in a volatile state where the East and the West are confronting each other. The world’s economic activities are also greatly affected by the breakthrough and innovations in technology. The less-than-ideal and less-than fair globalization will gradually become global localization. And as a result of COVID-19, Taiwan and the rest of the world’s lifestyle, social structure, and economy have been disrupted, and must be comprehensively reorganized and restructured. This is certainly a crisis, but in the midst of every crisis, lies great opportunity. So, what lies before you are countless opportunities for innovation, and exciting new challenges.
Because, for the first time, Taiwan is standing with the rest of the world on a new race-track. Therefore, I would like to congratulate you, you are entering a new era full of hope.
I graduated from NTU in 1969. It has been more than 50 years and today, I would like to share some of my life experiences and insights.
When I graduated from college, I was filled with ambition. I couldn’t wait to make my mark. I joined one of the largest trading companies in Taiwan, where I quickly realized the only materials I learned in school that came in handy are a little bit of international trade practices and English. Much of what I had learned about business management would only be relevant 10 or 20 years down the road, if I was lucky enough to become a senior executive.
What’s even more ironic is that I graduated with a degree in business, but after working for 2 years, I realize I really don’t like being a businessman, and I don’t like doing business. What I am actually interested in is building an enterprise.
Fortunately, I chose to listen to the faint voice in my heart and decided to enter the bicycle industry. I left my position at the best trading company in Taipei to join a small company with only 38 employees in Dajia to pursue my dream.
At the time, many people thought I must’ve broken my skull and gone crazy, but looking back, I have made a great decision in my life.
The process of starting my own enterprise has been very difficult, but I have learned many valuable lessons. The first thing I learned is that before you can do anything else, you have to learn to be a person of good character.
We must be honest, down to earth, selfless, self-serving, and altruistic for the common good.
Be grateful, respectful and caring at all times. To love the world and to understand that one person’s ability is limited and that is necessary to build interpersonal relationships of mutual trust and support; to be part of a group and to play as a team.
When it comes to work, you must work hard with a positive attitude. And don’t be afraid to fail, be brave enough to accept new challenges. Because every time you fail, it just means you are one step closer to success. Learning is an active process. We learn by doing. We must stay open-minded and curious, aspiring to lifelong learning; to explore challenges and pursue excellence.
Here I would like to give everyone a special graduation gift, which are my two tips for success:
First, always put yourself in the customer’s shoes, regardless of what you’re doing or what kind of job title you hold. Think for them, think about what else you can do to create more value for the customers.
Second, regardless of what you’re doing or what kind of job title you hold, always think about the company from the proprietor’s point of view; what can we do to build a healthier company that’s sustainable.
Even if you are going to forget everything I say later, that’ ok. Just remember to take your graduation gift at heart, because these two tips on “empathy “are sure to bring you unexpected blessings in the future!
Later on, I realized that fame and money are merely byproducts. The real product is you, what kind of person you have become and what have you achieved. When the real product is identified, fame and money will come naturally.
So I encourage you, when you look for a job, don’t be too concerned about the starting salary and benefits. Because as a matter of fact, as a newcomer, it is very difficult to make a big contribution in the first three years, so really the company is paying to give you training instead. So choose a meaningful industry and find a good company where you can improve and grow, that’s what’s important.
Thirdly, I learned that knowledge is not the same as wisdom. All of you have learned a lot and have a lot of valuable knowledge, but this knowledge must be practiced in order to produce experience, and in the process of accumulation, there will be different experiences of reflection, learning and empathy, especially from failure, slowly and surely all these experiences will be condensed into useful wisdom.
My personal development can be divided into three stages. I began as a I-shaped person with some basic skills in business management. Later on, due to my job and curiosity, I gained respected experience in technology development, manufacturing, branding, global marketing, and service and thus gradually becoming a T-shape multidisciplinary professional.
However, when I combined the future mission and direction of the company with my life goals, promoting it enthusiastically and actively, that is when I finally became a X-shape transdisciplinary leader.
Once the company became more stable, I was faced with the challenge of planning the vision, mission, positioning, and long-term strategy for the company. At this stage, I had some new insights.
First, you must be able to draw a “golden triangle”, an equilateral triangle is the most stable shape with strategy on top, tactics in the middle, and operations at the bottom. In other words, the strategy is to do the right thing, the tactics is the using the right methods, and operations is using the right methods to excel on doing the right things while constantly improving them.
To develop long-term strategies, it is necessary to have insight.
I enjoy looking at the globe, yes, the spherical model of Earth that sits on a stand and can be rotated. When you rotate the globe, you can see every place and country on it, its geographical location, its natural ecology, and you can think about its historical evolution and development, so that you can come up with an overall comprehensive concept.
When thinking about long-term strategy, it is not easy to come up with a groundbreaking idea, because “men are often blind in their own cause”
Therefore, when I think about long-term strategy, I imagine myself as if I were looking at a rotating Earth from a man-made satellite. With the height of the universe, the breadth of global vision and the length of historical evolution, you will be able think about it from the perspective of an observer, and then you may have a breakthrough. This is how the strategy and vision of “GIANT for the World” and “glocalization” brand management came about.
Another kind of insight is how to introduce the products and services to create additional value to people. This requires standing at the crossroads of humanity and technology, observing their changes carefully, and then using technological advances to provide the best solutions for the potential needs of humanity in a timely manner. This is how GIANT’s Cycling World continues to introduce innovative values to its products.
We have also developed a special philosophy in our business, which is “don’t be the first one, be the only one”. There are so many people in the world, but no two people share the same fingerprints, making each person unique and one of a kind. God created each person with purpose and has gifted us with the abilities and elements needed for success. All of which is in your heart, waiting for you to take them. Therefore, we should not just follow each other’s footstep or live under the expectations of someone else. Instead, we should find and give reigns to our talents, to truly be ourselves, to become the only one and live a brilliant life.
The same applies to businesses, you can’t just go with the flow, we must find what is right and suitable and what is meaningful and contributive to the world, this is what it means to be the only one. When you strive for excellence and strive to be the only one, you will find the right goals and the value of sustainability. If you try to be first in everything, you may pursue many unrealistic goals and either be destroyed by unbearable pressure or resort to doing whatever it takes, and end up on the wrong path.
I have learned two important lessons in my long quest to be the “only one”. When you are determined to do the right thing and you are unyielding and persistent in your struggle, the power of the universe will rise up and help you. In addition, if you do something meaningful and do it well with the power of life, you may change the world.
I urge you to listen to the small voice deep within you and let it guide you to find your talents and understand your destiny.
Do not seek to be the first but to be the only. Be true to yourself and be the only one. Live your life to the fullest and enjoy your life to the fullest!
Many people say that growing up in Taiwan, you must complete three challenges in your lifetime: climb to the top of Jade Mountain, swim across Sun Moon Lake, and ride a bike around the island. I think this is very true. Therefore, I suggest that after graduation, before you go to the military or get a job, you might as well join a bicycle tour around the island and spend nine days to carefully experience and appreciate the beauty of Taiwan. As a graduation gift to yourself, let the tires kiss the beautiful land where you were born and raised.
All the professors and parents must be thinking in regret, “Ah! Too bad! I didn’t ride my bike around the island when I graduated and now I’m already in my 50s and 60s…” But that’s okay! You are actually at the perfect age to start your adventurous life.
Our President of NTU, President Kuan bravely completed his dream bike tour around the island in January this year. Let’s give President Kuan a round of applause!
Some people think that Taiwan is small and has no future. Every year, the World Economic Forum conducts a survey and ranking of the strength of countries around the world. Let’s take a look at Taiwan’s ranking. There are 235 countries in the world and we rank 137th in terms of landmass, 57th in terms of population, 21st in terms of economy, 11th in terms of overall competitiveness, and 4th in terms of innovation. It’s true that Taiwan is not big, but we are strong and we have a promising future.
In the foreseeable future there will become a new situation in which the United States, Europe and Asia will be the three focal regions. Just like three circles, each of which prioritizes its own interests and is in long-term competition, confrontation and resistance, but at the same time has to maintain some necessary links with each other. And in each circle, drastic changes will be endless, just like a storm circle. These three storm circles are linked together to form a so-called PERFECT STORM.
The Economist magazine recently featured Taiwan on its cover, calling it the most dangerous place on earth, but at the same time the most critical place for the technology industry. Personally, I think that Taiwan may be in the most favorable position in the future, right in the middle of the three storm circles, the eye of the typhoon, which is the center point where these three circles are connected. This place seems dangerous, but in fact it is the safest, most favorable, and most critical place.
When the three storm circles are inevitably involved in a long-term competition, confrontation, and boycott - a zero-sum game. Taiwan does not belong to any of these circles, nor does it need to participate in these competitions, confrontations, and boycotts. Instead, situated in this critical position, I should begin to think about what kind of innovative value I can provide to the world.
Taiwan's efforts over the past 50 years have accumulated many rare and valuable strengths in technology development, manufacturing management, financial capital, social resources, and the democratic rule of law system. If the world is our stage, Taiwanese people who are proficient in Chinese and English, who have a deep understanding of Eastern and Western cultures, who are professional, who can cross borders, who are humble but not inferior, and who are confident but not arrogant, may be the best talents in the world.
The future cannot be predicted, but it can be created! 30 years ago, who could have predicted that Taiwan would produce globally renowned TSMC and GIANT?
TSMC is committed to the concept of being the only one, and has built up its strengths with its global foundry strategy, creating a high-quality and efficient supply chain and turning Taiwan into a technology island. With Taiwan as the core, TSMC is leading the worldwide semiconductor market.
GIANT also upholds the concept of "Be the only one" and leads the A-Team to turn Taiwan into the center of the world's premium bikes with the brand strategy of globalization and localization, and actively promotes the new culture of cycling, developing Taiwan into a cycling island and leading the world's cycling market with Taiwan as the core.
Here I also hope that we, NTU, can also lead the world with the concept of being the only one, and become the best cradle for the cultivation of the best talents in the world.
In the next 50 years, Taiwan will have the greatest opportunity ever! Therefore, Taiwan should get rid of the simple competitive mentality of the past and actively seek the long-term survival of the future, or go even further and ask itself: What can Taiwan contribute to the world? Taiwan should not just "secure Taiwan" in a conservative way, but should be more proactive with global outlook and establish a foothold in the Asia Pacific.
The last 50 years, for Taiwan, has been very difficult and significant. Fortunately, Taiwan pulled through and came out as one of the winners.
Now is the crucial moment to understand the past, delve into the present, and plan for the future. We must take the height of the universe, the breadth of our global vision, and the length of historical evolution, weigh the external situation, and measure our own strengths to reposition Taiwan and draw the golden triangle of strategy, tactics, and operations. Taiwan should be able to give full play to its strengths, create innovative value, and become the only one! Taiwan must become a good citizen of the world and continue to make more and better contributions to the world!
Graduates of 2021, among you are the future leaders of the country, the creators of global enterprises, and the core talents in all walks of life, the future is in your hands.
Finally, I would like to conclude with a quote from a famous scientist: Life is like riding a bicycle. To keep your balance, you must keep moving.
Do you know who this quote belongs to? That's right, Einstein!
Graduates of 2021, please get ready for your life adventure, get on your bikes and peddle courageously forward to create your own unique and exciting future. Again, congratulations and best wishes to each and every one of you for a healthy, happy, and meaningful life!
Thank you!
詳見:
https://www.facebook.com/NTUCommencement/posts/2718162161807541
.
#臺灣大學 #畢業典禮 #NTUCommencement2021 #貴賓致詞 #羅祥安
同時也有10000部Youtube影片,追蹤數超過2,910的網紅コバにゃんチャンネル,也在其Youtube影片中提到,...
「flow together中文」的推薦目錄:
- 關於flow together中文 在 國立臺灣大學 National Taiwan University Facebook 的最佳解答
- 關於flow together中文 在 Krenz Artwork Facebook 的最佳貼文
- 關於flow together中文 在 Musaubach Facebook 的精選貼文
- 關於flow together中文 在 コバにゃんチャンネル Youtube 的最佳解答
- 關於flow together中文 在 大象中醫 Youtube 的最佳解答
- 關於flow together中文 在 大象中醫 Youtube 的精選貼文
- 關於flow together中文 在 flow歌詞ff14的推薦與評價,YOUTUBE、PTT和網紅們這樣回答 的評價
- 關於flow together中文 在 flow歌詞ff14的推薦與評價,YOUTUBE、PTT和網紅們這樣回答 的評價
- 關於flow together中文 在 【強者】怎樣活用英文名詞?shower, purge 與rinse... - Facebook 的評價
- 關於flow together中文 在 Using OAuth 2.0 for Web Server Applications 的評價
flow together中文 在 Krenz Artwork Facebook 的最佳貼文
居然是英文版心得@0@
Cleared another Krenz Cushart online course!!
I now only found that I didn't write much description on the Painting course I attend last time, haha...
This is an interesting course regarding how you can design gesture and composition using lines, shapes and forms.
The course has designed to make sure the students understand the theories of composition as simple as possible. By introduce the basic elements of composition, this makes beginner less anxious about this part of knowledge. This also allows composition arrangement become more flexible.
For the dynamic gesture part, the course doesn't really teach you anatomy in details, they didn't even limit gesture on human. The course teach you how to identify gesture and design pose based on flow of body movement. And further extend as tool for composition in a visual, together with other elements such as clothes, hairs, accessories, tails etc. As long as the characters look reasonable, they doesn't restrict much on the drawing style, this grant the freedom of creation for students.
This course has helped me break some of the rigid form I've always had on drawing, didn't know I could draw human in these pose, and with composition in mind.
Throughout the course, the importance of references and create own visual library has mentioned so many time. This shows that other than receiving guidance from lectures, research and self study should not be neglected.
Using the existing character design for assignment that required to draw human are allowed. As long as you able to meet assignment requirement.
Whoever reading this may wonder if this course include colors as composition tool, no it didn't. The color part had included in Krenz Painting Course.
#KK魔法学院
中文版感想可以往我微博那里看(趁机宣传
https://www.weibo.com/6734013394
flow together中文 在 Musaubach Facebook 的精選貼文
我老婆在台北市新樂園藝術空間辦個展!這部影片內容是她給大家看些作品和介紹展覽的主題。開幕茶會時我要表演!歡迎大家來跟我們玩!My wife is having a solo exhibition in Taipei at SLY art space next November. In this video she is talking in Chinese about some of her paintings and introducing the theme of the exhibition. I'll be playing at the opening! You are all welcome to come and join us! ;)
風夜狂想曲 Rhapsody on a Windy Night
Marina Burana solo exhibition 明蓮花個展
2018.11.03 - 2018.11.25
Opening 開幕茶會:11.03 (六) 17:00
Venue 展覽地點:新樂園藝術空間 SLY art space
Address 地址:台北市中山區中山北路二段11巷15-2號
No. 15-2, Lane 11, Section 2, Zhong Shan N. Rd., Zhongshan District, Taipei, Taiwan.
https://www.facebook.com/marinaburana/videos/266696694051263/
聽得懂中文:看影片!
This video is in Chinese. For English: read below! :)
Este video está en chino. Si de verdad es muy "chino básico": acá más abajo pueden leer en español! ;)
*
Hello everyone! This little video is in Chinese. It's an invitation to my exhibition next November. In it, I talk a little bit about the paintings and this exhibition in particular. “Rhapsody on a Windy Night” is the title of a poem by T.S. Eliot. I chose this title as the name of my exhibition because I was inspired by this piece to paint this series of paintings. The poem talks about a person wandering through the night, lost in thought, a mere witness and also part of the world around him. It describes a struggle between the most transient of things, memory, and the physicality of existence, the material world. I translated this struggle into this series of paintings all dealing with being underwater as an analogy of being trapped in our memories. Their common thread is the flow of changing color, the intermingling of shapes and movement, just like memory, which "throws up high and dry a crowd of twisted things". This exhibition is very dear to me. The opening will be special since my husband, who is a musician, will perform for us and this is the third exhibition my baby boy will be attending (he attended the first one when in the womb). Also, I will make chocolate chip cookies! For me, every exhibition is a communal activity, an excuse to be together, to create bonds...so please come join us and let's have fun together!
*
Hola a todos! Este video está en chino. Es una invitación a mi exhibición de noviembre. En él hablo un poco de las pinturas y de la exhibición en sí. "Rapsodia de una noche de viento" es el título de un poema de T.S. Eliot que fue la fuente de inspiración para hacer estas pinturas. En el poema se presenta a una persona que deambula por la noche, perdida en sus pensamientos, un mero testigo de lo que ocurre a su alrededor y, a la vez, parte fundamental del entramado del mundo. El poema describe una lucha entre la memoria -condenada a escurrirse, transformarse, desaparecer- y lo físico de la existencia, el mundo material. Mi trabajo personal fue traducir esa lucha en esta serie de pinturas que tratan el tema de la vida, el movimiento bajo el agua como una analogía de estar atrapado en la memoria. El denominador común es el fluir del color, el entrelazado de formas y de movimiento, como la memoria, que "arroja y deja en seco una multitud de cosas retorcidas". Esta es una exhibición muy querida para mí. Mi marido va a tocar el piano en la apertura y mi bebito de 9 meses estará allí (esta es su tercera exhibición, a la primera asistió desde la panza). Va a ser algo muy familiar, muy cálido; hasta haré galletitas con chips de chocolate para todos! Para mí, cada exhibición es una forma de crear comunidad, una excusa para generar lazos humanos, así que los que estén en Taipei, bienvenidísimos a jugar un rato con nosotros!
-\-\-\-\-
風夜狂想曲 Rhapsody on a Windy Night
Marina Burana solo exhibition 明蓮花個展
2018.11.03 - 2018.11.25
Opening 茶會:11.03 (六) 17:00
Venue 展覽地點:新樂園藝術空間 SLY art space
Address 地址:台北市中山區中山北路二段11巷15-2號
No. 15-2, Lane 11, Section 2, Zhong Shan N. Rd., Zhongshan District, Taipei, Taiwan.
flow together中文 在 コバにゃんチャンネル Youtube 的最佳解答
flow together中文 在 大象中醫 Youtube 的最佳解答
flow together中文 在 大象中醫 Youtube 的精選貼文
flow together中文 在 Using OAuth 2.0 for Web Server Applications 的美食出口停車場
This document explains how web server applications use Google API Client Libraries or Google
OAuth 2.0 endpoints to implement OAuth 2.0 authorization to access
the YouTube Data API.
OAuth 2.0 allows users to share specific data with an application while keeping their
usernames, passwords, and other information private.
For example, an application can use OAuth 2.0 to obtain permission
to upload videos to a user's YouTube channel.
This OAuth 2.0 flow is specifically for user authorization. It is designed for applications
that can store confidential information and maintain state. A properly authorized web server
application can access an API while the user interacts with the application or after the user
has left the application.
Web server applications frequently also use
service accounts to authorize API requests, particularly when calling Cloud APIs to access
project-based data rather than user-specific data. Web server applications can use service
accounts in conjunction with user authorization.
Note that the YouTube Data API supports the service account flow only for
YouTube content owners that own and manage multiple YouTube channels.
Specifically, content owners can use service accounts to call API methods that
support the onBehalfOfContentOwner
request parameter.
Note: Given the security implications of getting the implementation
correct, we strongly encourage you to use OAuth 2.0 libraries when interacting with Google's
OAuth 2.0 endpoints. It is a best practice to use well-debugged code provided by others, and
it will help you protect yourself and your users. For more information, see
Client libraries.
Client libraries
The language-specific examples on this page use
Google API Client Libraries to implement
OAuth 2.0 authorization. To run the code samples, you must first install the
client library for your language.
When you use a Google API Client Library to handle your application's OAuth 2.0 flow, the client
library performs many actions that the application would otherwise need to handle on its own. For
example, it determines when the application can use or refresh stored access tokens as well as
when the application must reacquire consent. The client library also generates correct redirect
URLs and helps to implement redirect handlers that exchange authorization codes for access tokens.
Google API Client Libraries for server-side applications are available for the following languages:
Go
Java
.NET
Node.js
PHP
Python
Ruby
Important: The Google API client
library for JavaScript and Sign In With Google are
only intended to handle OAuth 2.0 in the user's browser. If you want to use JavaScript on the
server-side to manage OAuth 2.0 interactions with Google, consider using the
Node.js library on your back-end platform.
Prerequisites
Enable APIs for your project
Any application that calls Google APIs needs to enable those APIs in the
API Console.
To enable an API for your project:
Open the API Library in the
Google API Console.
If prompted, select a project, or create a new one. Use the Library page to find and enable the YouTube Data API. Find any other
APIs that your application will use and enable those, too.
Create authorization credentials
Any application that uses OAuth 2.0 to access Google APIs must have authorization credentials
that identify the application to Google's OAuth 2.0 server. The following steps explain how to
create credentials for your project. Your applications can then use the credentials to access APIs
that you have enabled for that project.
Go to the Clients page.
Click Create Client.
Select the Web application application type.
Fill in the form and click Create. Applications that use languages and frameworks
like PHP, Java, Python, Ruby, and .NET must specify authorized redirect URIs. The
redirect URIs are the endpoints to which the OAuth 2.0 server can send responses. These
endpoints must adhere to Google’s validation rules.
For testing, you can specify URIs that refer to the local machine, such as
http://localhost:8080
. With that in mind, please note that all of the
examples in this document use http://localhost:8080
as the redirect URI.
We recommend that you design your app's auth endpoints so
that your application does not expose authorization codes to other resources on the
page.
After creating your credentials, download the client_secret.json file from the
API Console. Securely store the file in a location that only
your application can access.
publicly-accessible location. In addition, if you share the source code to your
application — for example, on GitHub — store the client_secret.json file
outside of your source tree to avoid inadvertently sharing your client credentials.
Identify access scopes
Scopes enable your application to only request access to the resources that it needs while also
enabling users to control the amount of access that they grant to your application. Thus, there
may be an inverse relationship between the number of scopes requested and the likelihood of
obtaining user consent.
Before you start implementing OAuth 2.0 authorization, we recommend that you identify the scopes
that your app will need permission to access.
We also recommend that your application request access to authorization scopes via an
incremental authorization process, in which your application
requests access to user data in context. This best practice helps users to more easily understand
why your application needs the access it is requesting.
The YouTube Data API v3 uses the following scopes:
Scopes
https://www.googleapis.com/auth/youtubeManage your YouTube account
https://www.googleapis.com/auth/youtube.channel-memberships.creatorSee a list of your current active channel members, their current level, and when they became a member
https://www.googleapis.com/auth/youtube.force-sslSee, edit, and permanently delete your YouTube videos, ratings, comments and captions
https://www.googleapis.com/auth/youtube.readonlyView your YouTube account
https://www.googleapis.com/auth/youtube.uploadManage your YouTube videos
https://www.googleapis.com/auth/youtubepartnerView and manage your assets and associated content on YouTube
https://www.googleapis.com/auth/youtubepartner-channel-auditView private information of your YouTube channel relevant during the audit process with a YouTube partner
The OAuth 2.0 API Scopes document contains a full
list of scopes that you might use to access Google APIs.
If your public application uses scopes that permit access to
certain user data, it must complete a verification process. If you see unverified
app on the screen when testing your application, you must submit a
verification request to remove it. Find out more about
unverified apps
and get answers to
frequently asked questions about app verification in the Help Center.
Language-specific requirements
To run any of the code samples in this document, you'll need a Google account, access to the
Internet, and a web browser. If you are using one of the API client libraries, also see the
language-specific requirements below.
To run the PHP code samples in this document, you'll need:
PHP 8.0 or greater with the command-line interface (CLI) and JSON extension installed.
The Composer dependency management tool.
The Google APIs Client Library for PHP:
composer require google/apiclient:^2.15.0
See Google APIs Client Library for
PHP for more information.
To run the Python code samples in this document, you'll need:
Python 3.7 or greater
The pip package management tool.
The Google APIs Client Library for Python 2.0 release:
pip install --upgrade google-api-python-client
The
google-auth
, google-auth-oauthlib
, andgoogle-auth-httplib2
for user authorization.
pip install --upgrade google-auth google-auth-oauthlib google-auth-httplib2
The Flask Python web application framework.
pip install --upgrade flask
The
requests
HTTP library.
pip install --upgrade requests
Review the Google API Python client library
release note
if you aren't able to upgrade python and associated migration guide.
To run the Ruby code samples in this document, you'll need:
Ruby 2.6 or greater
The Google Auth Library for Ruby:
gem install googleauth
The Sinatra Ruby web application framework.
gem install sinatra
Node.js
To run the Node.js code samples in this document, you'll need:
The maintenance LTS, active LTS, or current release of Node.js.
The Google APIs Node.js Client:
npm install googleapis crypto express express-session
HTTP/REST
You do not need to install any libraries to be able to directly call the OAuth 2.0
endpoints.
Obtaining OAuth 2.0 access tokens
The following steps show how your application interacts with Google's OAuth 2.0 server to obtain
a user's consent to perform an API request on the user's behalf. Your application must have that
consent before it can execute a Google API request that requires user authorization.
The list below quickly summarizes these steps:
Your application identifies the permissions it needs.
Your application redirects the user to Google along with the list of requested
permissions.
The user decides whether to grant the permissions to your application.
Your application finds out what the user decided.
If the user granted the requested permissions, your application retrieves tokens needed to
make API requests on the user's behalf.
Step 1: Set authorization parameters
Your first step is to create the authorization request. That request sets parameters that
identify your application and define the permissions that the user will be asked to grant to
your application.
If you use a Google client library for OAuth 2.0 authentication and authorization, you
create and configure an object that defines these parameters.
If you call the Google OAuth 2.0 endpoint directly, you'll generate a URL and set the
parameters on that URL.
The tabs below define the supported authorization parameters for web server applications. The
language-specific examples also show how to use a client library or authorization library to
configure an object that sets those parameters.
The following code snippet creates a Google\Client()
object, which defines the
parameters in the authorization request.
That object uses information from your client_secret.json file to identify your
application. (See creating authorization credentials for more about
that file.) The object also identifies the scopes that your application is requesting permission
to access and the URL to your application's auth endpoint, which will handle the response from
Google's OAuth 2.0 server. Finally, the code sets the optional access_type
and
include_granted_scopes
parameters.
For example, this code requests offline access to manage a user's YouTube
account:
use Google\Client;$client = new Client();// Required, call the setAuthConfig function to load authorization credentials fromPython
// client_secret.json file.
$client->setAuthConfig('client_secret.json');// Required, to set the scope value, call the addScope function
$client->addScope(GOOGLE_SERVICE_YOUTUBE::YOUTUBE_FORCE_SSL);// Required, call the setRedirectUri function to specify a valid redirect URI for the
// provided client_id
$client->setRedirectUri('http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php');// Recommended, offline access will give you both an access and refresh token so that
// your app can refresh the access token without user interaction.
$client->setAccessType('offline');// Recommended, call the setState function. Using a state value can increase your assurance that
// an incoming connection is the result of an authentication request.
$client->setState($sample_passthrough_value);// Optional, if your application knows which user is trying to authenticate, it can use this
// parameter to provide a hint to the Google Authentication Server.
$client->setLoginHint('hint@example.com');// Optional, call the setPrompt function to set "consent" will prompt the user for consent
$client->setPrompt('consent');// Optional, call the setIncludeGrantedScopes function with true to enable incremental
// authorization
$client->setIncludeGrantedScopes(true);
The following code snippet uses the google-auth-oauthlib.flow
module to construct
the authorization request.
The code constructs a Flow
object, which identifies your application using
information from the client_secret.json file that you downloaded after
creating authorization credentials. That object also identifies the
scopes that your application is requesting permission to access and the URL to your application's
auth endpoint, which will handle the response from Google's OAuth 2.0 server. Finally, the code
sets the optional access_type
and include_granted_scopes
parameters.
For example, this code requests offline access to manage a user's YouTube
account:
import google.oauth2.credentialsRuby
import google_auth_oauthlib.flow# Required, call the from_client_secrets_file method to retrieve the client ID from a
# client_secret.json file. The client ID (from that file) and access scopes are required. (You can
# also use the from_client_config method, which passes the client configuration as it originally
# appeared in a client secrets file but doesn't access the file itself.)
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file('client_secret.json',
scopes=['https://www.googleapis.com/auth/youtube.force-ssl'])# Required, indicate where the API server will redirect the user after the user completes
# the authorization flow. The redirect URI is required. The value must exactly
# match one of the authorized redirect URIs for the OAuth 2.0 client, which you
# configured in the API Console. If this value doesn't match an authorized URI,
# you will get a 'redirect_uri_mismatch' error.
flow.redirect_uri = 'https://www.example.com/oauth2callback'# Generate URL for request to Google's OAuth 2.0 server.
# Use kwargs to set optional request parameters.
authorization_url, state = flow.authorization_url(
# Recommended, enable offline access so that you can refresh an access token without
# re-prompting the user for permission. Recommended for web server apps.
access_type='offline',
# Optional, enable incremental authorization. Recommended as a best practice.
include_granted_scopes='true',
# Optional, if your application knows which user is trying to authenticate, it can use this
# parameter to provide a hint to the Google Authentication Server.
login_hint='hint@example.com',
# Optional, set prompt to 'consent' will prompt the user for consent
prompt='consent')
Use the client_secrets.json file that you created to configure a client object in your
application. When you configure a client object, you specify the scopes your application needs to
access, along with the URL to your application's auth endpoint, which will handle the response
from the OAuth 2.0 server.
For example, this code requests offline access to manage a user's YouTube
account:
require 'googleauth'
require 'googleauth/web_user_authorizer'
require 'googleauth/stores/redis_token_store'require 'google/apis/youtube_v3'# Required, call the from_file method to retrieve the client ID from a
# client_secret.json file.
client_id = Google::Auth::ClientId.from_file('/path/to/client_secret.json')# Required, scope value
scope = 'https://www.googleapis.com/auth/youtube.force-ssl'# Required, Authorizers require a storage instance to manage long term persistence of
# access and refresh tokens.
token_store = Google::Auth::Stores::RedisTokenStore.new(redis: Redis.new)# Required, indicate where the API server will redirect the user after the user completes
# the authorization flow. The redirect URI is required. The value must exactly
# match one of the authorized redirect URIs for the OAuth 2.0 client, which you
# configured in the API Console. If this value doesn't match an authorized URI,
# you will get a 'redirect_uri_mismatch' error.
callback_uri = '/oauth2callback'# To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI
# from the client_secret.json file. To get these credentials for your application, visit
# https://console.cloud.google.com/apis/credentials.
authorizer = Google::Auth::WebUserAuthorizer.new(client_id, scope,
token_store, callback_uri)
Your application uses the client object to perform OAuth 2.0 operations, such as generating
authorization request URLs and applying access tokens to HTTP requests.
The following code snippet creates a google.auth.OAuth2
object, which defines the
parameters in the authorization request.
That object uses information from your client_secret.json file to identify your application. To
ask for permissions from a user to retrieve an access token, you redirect them to a consent page.
To create a consent page URL:
const {google} = require('googleapis');
const crypto = require('crypto');
const express = require('express');
const session = require('express-session');/**
* To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI
* from the client_secret.json file. To get these credentials for your application, visit
* https://console.cloud.google.com/apis/credentials.
*/
const oauth2Client = new google.auth.OAuth2(
YOUR_CLIENT_ID,
YOUR_CLIENT_SECRET,
YOUR_REDIRECT_URL
);// Access scopes for YouTube API
const scopes = [
'https://www.googleapis.com/auth/youtube.force-ssl'
];// Generate a secure random state value.
const state = crypto.randomBytes(32).toString('hex');// Store state in the session
req.session.state = state;// Generate a url that asks permissions for the Drive activity and Google Calendar scope
const authorizationUrl = oauth2Client.generateAuthUrl({
// 'online' (default) or 'offline' (gets refresh_token)
access_type: 'offline',
/** Pass in the scopes array defined above.
* Alternatively, if only one scope is needed, you can pass a scope URL as a string */
scope: scopes,
// Enable incremental authorization. Recommended as a best practice.
include_granted_scopes: true,
// Include the state parameter to reduce the risk of CSRF attacks.
state: state
});
Important Note - The refresh_token
is only returned on the first
authorization. More details
here.
Google's OAuth 2.0 endpoint is at https://accounts.google.com/o/oauth2/v2/auth
. This
endpoint is accessible only over HTTPS. Plain HTTP connections are refused.
The Google authorization server supports the following query string parameters for web
server applications:
Parameters
client_id
Required
The client ID for your application. You can find this value in the
Cloud Console
Clients page.
redirect_uri
Required
Determines where the API server redirects the user after the user completes the
authorization flow. The value must exactly match one of the authorized redirect URIs for
the OAuth 2.0 client, which you configured in your client's
Cloud Console
Clients page. If this value doesn't match an
authorized redirect URI for the provided client_id
you will get a
redirect_uri_mismatch
error.
Note that the http
or https
scheme, case, and trailing slash
('/
') must all match.
response_type
Required
Determines whether the Google OAuth 2.0 endpoint returns an authorization code.
Set the parameter value to code
for web server applications.
scope
Required
A
space-delimited
list of scopes that identify the resources that your application could access on the
user's behalf. These values inform the consent screen that Google displays to the
user.
Scopes enable your application to only request access to the resources that it needs
while also enabling users to control the amount of access that they grant to your
application. Thus, there is an inverse relationship between the number of scopes requested
and the likelihood of obtaining user consent.
The YouTube Data API v3 uses the following scopes:
Scopes
https://www.googleapis.com/auth/youtubeManage your YouTube account
https://www.googleapis.com/auth/youtube.channel-memberships.creatorSee a list of your current active channel members, their current level, and when they became a member
https://www.googleapis.com/auth/youtube.force-sslSee, edit, and permanently delete your YouTube videos, ratings, comments and captions
https://www.googleapis.com/auth/youtube.readonlyView your YouTube account
https://www.googleapis.com/auth/youtube.uploadManage your YouTube videos
https://www.googleapis.com/auth/youtubepartnerView and manage your assets and associated content on YouTube
https://www.googleapis.com/auth/youtubepartner-channel-auditView private information of your YouTube channel relevant during the audit process with a YouTube partner
The OAuth 2.0 API Scopes document provides
a full list of scopes that you might use to access Google APIs.
We recommend that your application request access to authorization scopes in context
whenever possible. By requesting access to user data in context, via
incremental authorization, you help users to more easily
understand why your application needs the access it is requesting.
access_type
Recommended
Indicates whether your application can refresh access tokens when the user is not present
at the browser. Valid parameter values are online
, which is the default
value, and offline
.
Set the value to offline
if your application needs to refresh access tokens
when the user is not present at the browser. This is the method of refreshing access
tokens described later in this document. This value instructs the Google authorization
server to return a refresh token and an access token the first time that your
application exchanges an authorization code for tokens.
state
Recommended
Specifies any string value that your application uses to maintain state between your
authorization request and the authorization server's response.
The server returns the exact value that you send as a name=value
pair in the
URL query component (?
) of the
redirect_uri
after the user consents to or denies your application's
access request.
You can use this parameter for several purposes, such as directing the user to the
correct resource in your application, sending nonces, and mitigating cross-site request
forgery. Since your redirect_uri
can be guessed, using a state
value can increase your assurance that an incoming connection is the result of an
authentication request. If you generate a random string or encode the hash of a cookie or
another value that captures the client's state, you can validate the response to
additionally ensure that the request and response originated in the same browser,
providing protection against attacks such as
cross-site request
forgery. See the
OpenID Connect
documentation for an example of how to create and confirm a state
token.
Important: The OAuth client must prevent CSRF as called out in the
OAuth2 Specification
. One way to achieve this is by using the
state
parameter to maintainstate between your authorization request and the authorization server's response.
include_granted_scopes
Optional
Enables applications to use incremental authorization to request access to additional
scopes in context. If you set this parameter's value to true
and the
authorization request is granted, then the new access token will also cover any scopes to
which the user previously granted the application access. See the
incremental authorization section for examples.
enable_granular_consent
Optional
Defaults to true
. If set to false
,
more
granular Google Account permissions
will be disabled for OAuth client IDs created before 2019. No effect for newer
OAuth client IDs, since more granular permissions is always enabled for them.
When Google enables granular permissions for an application, this parameter will no
longer have any effect.
login_hint
Optional
If your application knows which user is trying to authenticate, it can use this parameter
to provide a hint to the Google Authentication Server. The server uses the hint to
simplify the login flow either by prefilling the email field in the sign-in form or by
selecting the appropriate multi-login session.
Set the parameter value to an email address or sub
identifier, which is
equivalent to the user's Google ID.
prompt
Optional
A space-delimited, case-sensitive list of prompts to present the user. If you don't
specify this parameter, the user will be prompted only the first time your project
requests access. See
Prompting re-consent for more information.
Possible values are:
none
Do not display any authentication or consent screens. Must not be specified with
other values.
consent
Prompt the user for consent.
select_account
Prompt the user to select an account.
Step 2: Redirect to Google's OAuth 2.0 server
Redirect the user to Google's OAuth 2.0 server to initiate the authentication and
authorization process. Typically, this occurs when your application first needs to access the
user's data. In the case of incremental authorization, this
step also occurs when your application first needs to access additional resources that it does
not yet have permission to access.
Generate a URL to request access from Google's OAuth 2.0 server:
$auth_url = $client->createAuthUrl();
Redirect the user to
$auth_url
:header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
Python
This example shows how to redirect the user to the authorization URL using the Flask web
application framework:
return flask.redirect(authorization_url)Ruby
Generate a URL to request access from Google's OAuth 2.0 server:
auth_uri = authorizer.get_authorization_url(request: request)
Redirect the user to
auth_uri
.Node.js
Use the generated URL
authorizationUrl
from Step 1generateAuthUrl
method to request access from Google's OAuth 2.0 server.Redirect the user to
authorizationUrl
.res.redirect(authorizationUrl);
HTTP/REST Sample redirect to Google's authorization server
The sample URL below requests offline access
(access_type=offline
) to a scope that permits access to view
the user's YouTube account. It uses incremental authorization to ensure that
the new access token covers any scopes to which the user previously granted
the application access. The URL also sets values for the required
redirect_uri
, response_type
, and
client_id
parameters as well as for the state
parameter. The URL contains line breaks and spaces for readability.
https://accounts.google.com/o/oauth2/v2/auth?
scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyoutube.readonly&
access_type=offline&
include_granted_scopes=true&
state=state_parameter_passthrough_value&
redirect_uri=http%3A%2F%2Flocalhost%2Foauth2callback&
response_type=code&
client_id=client_id
After you create the request URL, redirect the user to it.
Google's OAuth 2.0 server authenticates the user and obtains consent from the user for your
application to access the requested scopes. The response is sent back to your application
using the redirect URL you specified.
In this step, the user decides whether to grant your application the requested access. At this
stage, Google displays a consent window that shows the name of your application and the Google API
services that it is requesting permission to access with the user's authorization credentials and
a summary of the scopes of access to be granted. The
user can then consent to grant access to one or more scopes requested by your application or
refuse the request.
Your application doesn't need to do anything at this stage as it waits for the response from
Google's OAuth 2.0 server indicating whether any access was granted. That response is explained in
the following step.
Errors
Requests to Google's OAuth 2.0 authorization endpoint may display user-facing error messages
instead of the expected authentication and authorization flows. Common error codes and suggested
resolutions are listed below.
admin_policy_enforced
The Google Account is unable to authorize one or more scopes requested due to the policies of
their Google Workspace administrator. See the Google Workspace Admin help article
Control which third-party & internal apps access Google Workspace data
for more information about how an administrator may restrict access to all scopes or sensitive and
restricted scopes until access is explicitly granted to your OAuth client ID.
disallowed_useragent
The authorization endpoint is displayed inside an embedded user-agent disallowed by Google's
OAuth 2.0 Policies.
Android
Android developers may encounter this error message when opening authorization requests in
android.webkit.WebView
.
Developers should instead use Android libraries such as
Google Sign-In for Android or OpenID Foundation's
AppAuth for Android.
Web developers may encounter this error when an Android app opens a general web link in an
embedded user-agent and a user navigates to Google's OAuth 2.0 authorization endpoint from
your site. Developers should allow general links to open in the default link handler of the
operating system, which includes both
Android App Links
handlers or the default browser app. The
Android Custom Tabs
library is also a supported option.
iOS
iOS and macOS developers may encounter this error when opening authorization requests in
WKWebView
.
Developers should instead use iOS libraries such as
Google Sign-In for iOS or OpenID Foundation's
AppAuth for iOS.
Web developers may encounter this error when an iOS or macOS app opens a general web link in
an embedded user-agent and a user navigates to Google's OAuth 2.0 authorization endpoint from
your site. Developers should allow general links to open in the default link handler of the
operating system, which includes both
Universal Links
handlers or the default browser app. The
SFSafariViewController
library is also a supported option.
org_internal
The OAuth client ID in the request is part of a project limiting access to Google Accounts in a
specific
Google Cloud Organization.
For more information about this configuration option see the
User type
section in the Setting up your OAuth consent screen help article.
invalid_client
The OAuth client secret is incorrect. Review the
OAuth client
configuration, including the client ID and secret used for this request.
invalid_grant
When refreshing an access token or using
incremental authorization, the token may have expired or has
been invalidated.
Authenticate the user again and ask for user consent to obtain new tokens. If you are continuing
to see this error, ensure that your application has been configured correctly and that you are
using the correct tokens and parameters in your request. Otherwise, the user account may have
been deleted or disabled.
redirect_uri_mismatch
The redirect_uri
passed in the authorization request does not match an authorized
redirect URI for the OAuth client ID. Review authorized redirect URIs in the
Google Cloud Console
Clients page.
The redirect_uri
parameter may refer to the OAuth out-of-band (OOB) flow that has
been deprecated and is no longer supported. Refer to the
migration guide to update your
integration.
invalid_request
There was something wrong with the request you made. This could be due to a number of reasons:
The request was not properly formatted
The request was missing required parameters
The request uses an authorization method that Google doesn't support. Verify your OAuth
integration uses a recommended integration method
Step 4: Handle the OAuth 2.0 server response Important:
Before handling the OAuth 2.0 response on the server, you should confirm that the
state
received from Google matches the state
sent in theauthorization request. This verification helps to ensure that the user, not a malicious
script, is making the request and reduces the risk of
CSRF attacks.
The OAuth 2.0 server responds to your application's access request by using the URL specified
in the request.
If the user approves the access request, then the response contains an authorization code. If
the user does not approve the request, the response contains an error message. The
authorization code or error message that is returned to the web server appears on the query
string, as shown below:
An error response:
https://oauth2.example.com/auth?error=access_denied
An authorization code response:
Important: If your response endpoint renders an
https://oauth2.example.com/auth?code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7
HTML page, any resources on that page will be able to see the authorization code in the URL.
Scripts can read the URL directly, and the URL in the
Referer
HTTP header may besent to any or all resources on the page.
Carefully consider whether you want to send authorization credentials to all resources on
that page (especially third-party scripts such as social plugins and analytics). To avoid
this issue, we recommend that the server first handle the request, then redirect to another
URL that doesn't include the response parameters.
You can test this flow by clicking on the following sample URL, which requests
read-only access to view metadata for files in your Google Drive and read-only
access to view your Google Calendar events:
https://accounts.google.com/o/oauth2/v2/auth?
scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyoutube.readonly&
access_type=offline&
include_granted_scopes=true&
state=state_parameter_passthrough_value&
redirect_uri=http%3A%2F%2Flocalhost%2Foauth2callback&
response_type=code&
client_id=client_id
After completing the OAuth 2.0 flow, you should be redirected to
http://localhost/oauth2callback
, which will likely yield a
404 NOT FOUND
error unless your local machine serves a file at that address. The
next step provides more detail about the information returned in the URI when the user is
redirected back to your application.
tokens
After the web server receives the authorization code, it can exchange the authorization code
for an access token.
To exchange an authorization code for an access token, use the
fetchAccessTokenWithAuthCode
method:
$access_token = $client->fetchAccessTokenWithAuthCode($_GET['code']);
Python On your callback page, use the google-auth
library to verify the authorization
server response. Then, use the flow.fetch_token
method to exchange the authorization
code in that response for an access token:
state = flask.session['state']Ruby
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
'client_secret.json',
scopes=['https://www.googleapis.com/auth/youtube.force-ssl'],
state=state)
flow.redirect_uri = flask.url_for('oauth2callback', _external=True)authorization_response = flask.request.url
flow.fetch_token(authorization_response=authorization_response)# Store the credentials in the session.
# ACTION ITEM for developers:
# Store user's access and refresh tokens in your data store if
# incorporating this code into your real app.
credentials = flow.credentials
flask.session['credentials'] = {
'token': credentials.token,
'refresh_token': credentials.refresh_token,
'token_uri': credentials.token_uri,
'client_id': credentials.client_id,
'client_secret': credentials.client_secret,
'granted_scopes': credentials.granted_scopes}
On your callback page, use the googleauth
library to verify the authorization server
response. Use the authorizer.handle_auth_callback_deferred
method to save the
authorization code and redirect back to the URL that originally requested authorization. This
defers the exchange of the code by temporarily stashing the results in the user's session.
target_url = Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred(request)Node.js
redirect target_url
To exchange an authorization code for an access token, use the getToken
method:
const url = require('url');// Receive the callback from Google's OAuth 2.0 server.HTTP/REST
app.get('/oauth2callback', async (req, res) => {
let q = url.parse(req.url, true).query; if (q.error) { // An error response e.g. error=access_denied
console.log('Error:' + q.error);
} else if (q.state !== req.session.state) { //check state value
console.log('State mismatch. Possible CSRF attack');
res.end('State mismatch. Possible CSRF attack');
} else { // Get access and refresh tokens (if access_type is offline) let { tokens } = await oauth2Client.getToken(q.code);
oauth2Client.setCredentials(tokens);
});
To exchange an authorization code for an access token, call the
https://oauth2.googleapis.com/token
endpoint and set the following parameters:
Fields
client_id
The client ID obtained from the Cloud Console
Clients page.
client_secret
The client secret obtained from the Cloud Console
Clients page.
code
The authorization code returned from the initial request.
grant_type
As defined in the OAuth 2.0
specification, this field's value must be set to
authorization_code
.redirect_uri
One of the redirect URIs listed for your project in the
Cloud Console
Clients page for the given
client_id
.The following snippet shows a sample request:
POST /token HTTP/1.1
Host: oauth2.googleapis.com
Content-Type: application/x-www-form-urlencodedcode=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&
client_id=your_client_id&
client_secret=your_client_secret&
redirect_uri=https%3A//oauth2.example.com/code&
grant_type=authorization_code
Google responds to this request by returning a JSON object that contains a short-lived access
token and a refresh token. Note that the refresh token is only returned if your application set the access_type
parameter to offline
in the initial request to Google's
authorization server.
The response contains the following fields:
Fields
access_token
The token that your application sends to authorize a Google API request.
expires_in
The remaining lifetime of the access token in seconds.
refresh_token
A token that you can use to obtain a new access token. Refresh tokens are valid until the
user revokes access.
Again, this field is only present in this response if you set the
access_type
parameter to
offline
in the initial request to Google's authorization server.scope
The scopes of access granted by the
access_token
expressed as a list ofspace-delimited, case-sensitive strings.
token_type
The type of token returned. At this time, this field's value is always set to
Bearer
.Important: Your application should store both tokens in a secure,
long-lived location that is accessible between different invocations of your application. The
refresh token enables your application to obtain a new access token if the one that you have
expires. As such, if your application loses the refresh token, the user will need to repeat the
OAuth 2.0 consent flow so that your application can obtain a new refresh token.
The following snippet shows a sample response:
{Note: Your application should ignore any unrecognized fields included in
"access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
"expires_in": 3920,
"token_type": "Bearer",
"scope": "https://www.googleapis.com/auth/youtube.force-ssl",
"refresh_token": "1//xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI"
}
the response.
Errors
When exchanging the authorization code for an access token you may encounter the following
error instead of the expected response. Common error codes and suggested resolutions are
listed below.
invalid_grant
The supplied authorization code is invalid or in the wrong format. Request a new code by
restarting the OAuth process to prompt the user for consent
again.
Step 6: Check which scopes users granted
When requesting multiple scopes at once, users may not grant all scopes your app
requests. Your app should always check which scopes were granted by the user and handle
any denial of scopes by disabling relevant features. Review
How to handle granular permissions
for more information.
To check which scopes the user has granted, use the getGrantedScope()
method:
// Space-separated string of granted scopes if it exists, otherwise null.Python
$granted_scopes = $client->getOAuth2Service()->getGrantedScope();
The returned credentials
object has a granted_scopes
property,
which is a list of scopes the user has granted to your app.
credentials = flow.credentials
flask.session['credentials'] = {
'token': credentials.token,
'refresh_token': credentials.refresh_token,
'token_uri': credentials.token_uri,
'client_id': credentials.client_id,
'client_secret': credentials.client_secret,
'granted_scopes': credentials.granted_scopes}
Ruby
When requesting multiple scopes at once, check which scopes were granted through
the scope
property of the credentials
object.
# User authorized the request. Now, check which scopes were granted.Node.js
if credentials.scope.include?(Google::Apis::YoutubeV3::AUTH_YOUTUBE_FORCE_SSL)
# User authorized permission to see, edit, and permanently delete the
# YouTube videos, ratings, comments and captions.
# Calling the APIs, etc
else
# User didn't authorize the permission.
# Update UX and application accordingly
end
When requesting multiple scopes at once, check which scopes were granted through
the scope
property of the tokens
object.
// User authorized the request. Now, check which scopes were granted.HTTP/REST
if (tokens.scope.includes('https://www.googleapis.com/auth/youtube.force-ssl'))
{
// User authorized permission to see, edit, and permanently delete the
// YouTube videos, ratings, comments and captions.
// Calling the APIs, etc.
}
else
{
// User didn't authorize read-only Drive activity permission.
// Update UX and application accordingly
}
To check whether the user has granted your application access to a particular scope,
exam the scope
field in the access token response. The scopes of access granted by
the access_token expressed as a list of space-delimited, case-sensitive strings.
For example, the following sample access token response indicates that the user has granted your
application permission to see, edit, and permanently delete user's YouTube videos, ratings,
comments and captions:
{
"access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
"expires_in": 3920,
"token_type": "Bearer",
"scope": "https://www.googleapis.com/auth/youtube.force-ssl",
"refresh_token": "1//xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI"
}
Call Google APIs
Use the access token to call Google APIs by completing the following steps:
If you need to apply an access token to a new
Google\Client
object — forexample, if you stored the access token in a user session — use the
setAccessToken
method: $client->setAccessToken($access_token);
Build a service object for the API that you want to call. You build a service object by
providing an authorized
Google\Client
object to the constructor for the API youwant to call.
For example, to call the YouTube Data API:
$youtube = new Google_Service_YouTube($client);
Make requests to the API service using the
interface provided by the service object.
For example, to retrieve data about the authorized user's YouTube channel:
$channel = $youtube->channels->listChannels('snippet', array('mine' => $mine));
Python
After obtaining an access token, your application can use that token to authorize API requests on
behalf of a given user account or service account. Use the user-specific authorization credentials
to build a service object for the API that you want to call, and then use that object to make
authorized API requests.
Build a service object for the API that you want to call. You build a service object by
calling the
googleapiclient.discovery
library's build
method with thename and version of the API and the user credentials:
For example, to call version 3 of the YouTube Data API:
from googleapiclient.discovery import buildyoutube = build('youtube', 'v3', credentials=credentials)
Make requests to the API service using the
interface provided by the service object.
For example, to retrieve data about the authorized user's YouTube channel:
channel = youtube.channels().list(mine=True, part='snippet').execute()
Ruby
After obtaining an access token, your application can use that token to make API requests on
behalf of a given user account or service account. Use the user-specific authorization credentials
to build a service object for the API that you want to call, and then use that object to make
authorized API requests.
Build a service object for the API that you want to call.
For example, to call version 3 of the YouTube Data API:
youtube = Google::Apis::YoutubeV3::YouTubeService.new
Set the credentials on the service:
youtube.authorization = credentials
Make requests to the API service using the
interface
provided by the service object.
For example, to retrieve data about the authorized user's YouTube channel:
channel = youtube.list_channels(part, :mine => mine)
Alternately, authorization can be provided on a per-method basis by supplying the
options
parameter to a method:
channel = youtube.list_channels(part, :mine => mine, options: { authorization: auth_client })
Node.js
After obtaining an access token and setting it to the OAuth2
object, use the object
to call Google APIs. Your application can use that token to authorize API requests on behalf of
a given user account or service account. Build a service object for the API that you want to call.
For example, the following code uses the Google Drive API to list filenames in the user's Drive.
const { google } = require('googleapis');// Example of using YouTube API to list channels.HTTP/REST
var service = google.youtube('v3');
service.channels.list({
auth: oauth2Client,
part: 'snippet,contentDetails,statistics',
forUsername: 'GoogleDevelopers'
}, function (err, response) {
if (err) {
console.log('The API returned an error: ' + err);
return;
}
var channels = response.data.items;
if (channels.length == 0) {
console.log('No channel found.');
} else {
console.log('This channel\'s ID is %s. Its title is \'%s\', and ' +
'it has %s views.',
channels[0].id,
channels[0].snippet.title,
channels[0].statistics.viewCount);
}
});
After your application obtains an access token, you can use the token to make calls to a Google
API on behalf of a given
user account if the scope(s) of access required by the API have been granted. To do this, include
the access token in a request to the API by including either an access_token
query
parameter or an Authorization
HTTP header Bearer
value. When possible,
the HTTP header is preferable, because query strings tend to be visible in server logs. In most
cases you can use a client library to set up your calls to Google APIs (for example, when
calling the YouTube Data API).
Note that the YouTube Data API supports service accounts only for YouTube
content owners that own and manage multiple YouTube channels, such as record
labels and movie studios.
You can try out all the Google APIs and view their scopes at the
OAuth 2.0 Playground.
A call to the
youtube.channels
endpoint (the YouTube Data API) using the Authorization: Bearer
HTTP
header might look like the following. Note that you need to specify your own access token:
GET /youtube/v3/channels?part=snippet&mine=true HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer access_token
Here is a call to the same API for the authenticated user using the access_token
query string parameter:
GET https://www.googleapis.com/youtube/v3/channels?access_token=access_token&part=snippet&mine=true
curl
examplesYou can test these commands with the curl
command-line application. Here's an
example that uses the HTTP header option (preferred):
curl -H "Authorization: Bearer access_token" https://www.googleapis.com/youtube/v3/channels?part=snippet&mine=true
Or, alternatively, the query string parameter option:
curl https://www.googleapis.com/youtube/v3/channels?access_token=access_token&part=snippet&mine=true
Complete example
The following example prints a JSON-formatted object showing information
about a user's YouTube channel after the user authenticates and authorizes the
application to manage the user's YouTube account.
To run this example:
In the API Console, add the URL of the local machine to the
list of redirect URLs. For example, add
http://localhost:8080
.Create a new directory and change to it. For example:
mkdir ~/php-oauth2-example
cd ~/php-oauth2-example
Install the Google API Client
Library for PHP using Composer:
composer require google/apiclient:^2.15.0
Create the files
index.php
and oauth2callback.php
with thefollowing content.
Run the example with the PHP's built-in test web server:
php -S localhost:8080 ~/php-oauth2-example
index.php
<?phpoauth2callback.php
require_once __DIR__.'/vendor/autoload.php';session_start();$client = new Google\Client();
$client->setAuthConfig('client_secret.json');// User granted permission as an access token is in the session.
if (isset($_SESSION['access_token']) && $_SESSION['access_token'])
{
$client->setAccessToken($_SESSION['access_token']);
$youtube = new Google_Service_YouTube($client);
$channel = $youtube->channels->listChannels('snippet', array('mine' => $mine));
echo json_encode($channel);
}
else
{
// Redirect users to outh2call.php which redirects users to Google OAuth 2.0
$redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php';
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
?>
<?phpPython
require_once __DIR__.'/vendor/autoload.php';session_start();$client = new Google\Client();// Required, call the setAuthConfig function to load authorization credentials from
// client_secret.json file.
$client->setAuthConfigFile('client_secret.json');
$client->setRedirectUri('http://' . $_SERVER['HTTP_HOST']. $_SERVER['PHP_SELF']);// Required, to set the scope value, call the addScope function.
$client->addScope(GOOGLE_SERVICE_YOUTUBE::YOUTUBE_FORCE_SSL);// Enable incremental authorization. Recommended as a best practice.
$client->setIncludeGrantedScopes(true);// Recommended, offline access will give you both an access and refresh token so that
// your app can refresh the access token without user interaction.
$client->setAccessType("offline");// Generate a URL for authorization as it doesn't contain code and error
if (!isset($_GET['code']) && !isset($_GET['error']))
{
// Generate and set state value
$state = bin2hex(random_bytes(16));
$client->setState($state);
$_SESSION['state'] = $state; // Generate a url that asks permissions.
$auth_url = $client->createAuthUrl();
header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
}// User authorized the request and authorization code is returned to exchange access and
// refresh tokens.
if (isset($_GET['code']))
{
// Check the state value
if (!isset($_GET['state']) || $_GET['state'] !== $_SESSION['state']) {
die('State mismatch. Possible CSRF attack.');
} // Get access and refresh tokens (if access_type is offline)
$token = $client->fetchAccessTokenWithAuthCode($_GET['code']); /** Save access and refresh token to the session variables.
* ACTION ITEM: In a production app, you likely want to save the
* refresh token in a secure persistent storage instead. */
$_SESSION['access_token'] = $token;
$_SESSION['refresh_token'] = $client->getRefreshToken();
$redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/';
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}// An error response e.g. error=access_denied
if (isset($_GET['error']))
{
echo "Error: ". $_GET['error'];
}
?>
This example uses the Flask framework. It
runs a web application at http://localhost:8080
that lets you test the OAuth 2.0
flow. If you go to that URL, you should see five links:
Test an API request: This link points to a page that tries to execute a sample API
request. If necessary, it starts the authorization flow. If successful, the page displays the
API response.
Test the auth flow directly: This link points to a page that tries to send the user
through the authorization flow. The app requests permission to
submit authorized API requests on the user's behalf.
Revoke current credentials: This link points to a page that
revokes permissions that the user has already granted to the application.
Clear Flask session credentials: This link clears authorization credentials that are
stored in the Flask session. This lets you see what would happen if a user who had already
granted permission to your app tried to execute an API request in a new session. It also lets
you see the API response your app would get if a user had revoked permissions granted to your
app, and your app still tried to authorize a request with a revoked access token.
Note: To run this code locally, you must have followed the directions in
the prerequisites section, including setting
http://localhost:8080
as a valid redirect URI for your credentials and downloadingthe client_secret.json file for those credentials to your working directory.
# -*- coding: utf-8 -*-import osRuby
import flask
import requestsimport google.oauth2.credentials
import google_auth_oauthlib.flow
import googleapiclient.discovery# This variable specifies the name of a file that contains the OAuth 2.0
# information for this application, including its client_id and client_secret.
CLIENT_SECRETS_FILE = "client_secret.json"# The OAuth 2.0 access scope allows for access to the
# authenticated user's account and requires requests to use an SSL connection.
SCOPES = ['https://www.googleapis.com/auth/youtube.force-ssl']
API_SERVICE_NAME = 'youtube'
API_VERSION = 'v3'app = flask.Flask(__name__)
# Note: A secret key is included in the sample so that it works.
# If you use this code in your application, replace this with a truly secret
# key. See https://flask.palletsprojects.com/quickstart/#sessions.
app.secret_key = 'REPLACE ME - this value is here as a placeholder.'@app.route('/')
def index():
return print_index_table()@app.route('/test')
def test_api_request():
if 'credentials' not in flask.session:
return flask.redirect('authorize') # Load credentials from the session.
credentials = google.oauth2.credentials.Credentials(
**flask.session['credentials']) youtube = googleapiclient.discovery.build(
API_SERVICE_NAME, API_VERSION, credentials=credentials) channel = youtube.channels().list(mine=True, part='snippet').execute() # Save credentials back to session in case access token was refreshed.
# ACTION ITEM: In a production app, you likely want to save these
# credentials in a persistent database instead.
flask.session['credentials'] = credentials_to_dict(credentials) return flask.jsonify(**channel)
@app.route('/authorize')
def authorize():
# Create flow instance to manage the OAuth 2.0 Authorization Grant Flow steps.
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
CLIENT_SECRETS_FILE, scopes=SCOPES) # The URI created here must exactly match one of the authorized redirect URIs
# for the OAuth 2.0 client, which you configured in the API Console. If this
# value doesn't match an authorized URI, you will get a 'redirect_uri_mismatch'
# error.
flow.redirect_uri = flask.url_for('oauth2callback', _external=True) authorization_url, state = flow.authorization_url(
# Enable offline access so that you can refresh an access token without
# re-prompting the user for permission. Recommended for web server apps.
access_type='offline',
# Enable incremental authorization. Recommended as a best practice.
include_granted_scopes='true') # Store the state so the callback can verify the auth server response.
flask.session['state'] = state return flask.redirect(authorization_url)@app.route('/oauth2callback')
def oauth2callback():
# Specify the state when creating the flow in the callback so that it can
# verified in the authorization server response.
state = flask.session['state'] flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
CLIENT_SECRETS_FILE, scopes=SCOPES, state=state)
flow.redirect_uri = flask.url_for('oauth2callback', _external=True) # Use the authorization server's response to fetch the OAuth 2.0 tokens.
authorization_response = flask.request.url
flow.fetch_token(authorization_response=authorization_response) # Store credentials in the session.
# ACTION ITEM: In a production app, you likely want to save these
# credentials in a persistent database instead.
credentials = flow.credentials
flask.session['credentials'] = credentials_to_dict(credentials) return flask.redirect(flask.url_for('test_api_request'))
@app.route('/revoke')
def revoke():
if 'credentials' not in flask.session:
return ('You need to <a href="/authorize">authorize</a> before ' +
'testing the code to revoke credentials.') credentials = google.oauth2.credentials.Credentials(
**flask.session['credentials']) revoke = requests.post('https://oauth2.googleapis.com/revoke',
params={'token': credentials.token},
headers = {'content-type': 'application/x-www-form-urlencoded'}) status_code = getattr(revoke, 'status_code')
if status_code == 200:
return('Credentials successfully revoked.' + print_index_table())
else:
return('An error occurred.' + print_index_table())@app.route('/clear')
def clear_credentials():
if 'credentials' in flask.session:
del flask.session['credentials']
return ('Credentials have been cleared.<br><br>' +
print_index_table())def credentials_to_dict(credentials):
return {'token': credentials.token,
'refresh_token': credentials.refresh_token,
'token_uri': credentials.token_uri,
'client_id': credentials.client_id,
'client_secret': credentials.client_secret,
'granted_scopes': credentials.granted_scopes}def print_index_table():
return ('<table>' +
'<tr><td><a href="/test">Test an API request</a></td>' +
'<td>Submit an API request and see a formatted JSON response. ' +
' Go through the authorization flow if there are no stored ' +
' credentials for the user.</td></tr>' +
'<tr><td><a href="/authorize">Test the auth flow directly</a></td>' +
'<td>Go directly to the authorization flow. If there are stored ' +
' credentials, you still might not be prompted to reauthorize ' +
' the application.</td></tr>' +
'<tr><td><a href="/revoke">Revoke current credentials</a></td>' +
'<td>Revoke the access token associated with the current user ' +
' session. After revoking credentials, if you go to the test ' +
' page, you should see an <code>invalid_grant</code> error.' +
'</td></tr>' +
'<tr><td><a href="/clear">Clear Flask session credentials</a></td>' +
'<td>Clear the access token currently stored in the user session. ' +
' After clearing the token, if you <a href="/test">test the ' +
' API request</a> again, you should go back to the auth flow.' +
'</td></tr></table>')if __name__ == '__main__':
# When running locally, disable OAuthlib's HTTPs verification.
# ACTION ITEM for developers:
# When running in production *do not* leave this option enabled.
os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' # This disables the requested scopes and granted scopes check.
# If users only grant partial request, the warning would not be thrown.
os.environ['OAUTHLIB_RELAX_TOKEN_SCOPE'] = '1' # Specify a hostname and port that are set as a valid redirect URI
# for your API project in the Google API Console.
app.run('localhost', 8080, debug=True)
This example uses the Sinatra framework.
require 'googleauth'Node.js
require 'googleauth/web_user_authorizer'
require 'googleauth/stores/redis_token_store'require 'google/apis/youtube_v3'require 'sinatra'configure do
enable :sessions # Required, call the from_file method to retrieve the client ID from a
# client_secret.json file.
set :client_id, Google::Auth::ClientId.from_file('/path/to/client_secret.json') # Required, scope value
# Access scopes for retrieving data about the user's YouTube channel.
scope = 'Google::Apis::YoutubeV3::AUTH_YOUTUBE_FORCE_SSL' # Required, Authorizers require a storage instance to manage long term persistence of
# access and refresh tokens.
set :token_store, Google::Auth::Stores::RedisTokenStore.new(redis: Redis.new) # Required, indicate where the API server will redirect the user after the user completes
# the authorization flow. The redirect URI is required. The value must exactly
# match one of the authorized redirect URIs for the OAuth 2.0 client, which you
# configured in the API Console. If this value doesn't match an authorized URI,
# you will get a 'redirect_uri_mismatch' error.
set :callback_uri, '/oauth2callback' # To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI
# from the client_secret.json file. To get these credentials for your application, visit
# https://console.cloud.google.com/apis/credentials.
set :authorizer, Google::Auth::WebUserAuthorizer.new(settings.client_id, settings.scope,
settings.token_store, callback_uri: settings.callback_uri)
endget '/' do
# NOTE: Assumes the user is already authenticated to the app
user_id = request.session['user_id'] # Fetch stored credentials for the user from the given request session.
# nil if none present
credentials = settings.authorizer.get_credentials(user_id, request) if credentials.nil?
# Generate a url that asks the user to authorize requested scope(s).
# Then, redirect user to the url.
redirect settings.authorizer.get_authorization_url(request: request)
end
# User authorized read-only YouTube Data API permission.
# Example of using YouTube Data API to list user's YouTube channel
youtube = Google::Apis::YoutubeV3::YouTubeService.new
channel = youtube.list_channels(part, :mine => mine, options: { authorization: auth_client })
"<pre>#{JSON.pretty_generate(channel.to_h)}</pre>"
end# Receive the callback from Google's OAuth 2.0 server.
get '/oauth2callback' do
# Handle the result of the oauth callback. Defers the exchange of the code by
# temporarily stashing the results in the user's session.
target_url = Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred(request)
redirect target_url
end
To run this example:
In the API Console, add the URL of the
local machine to the list of redirect URLs. For example, add
http://localhost
.Make sure you have maintenance LTS, active LTS, or current release of
Node.js installed.
Create a new directory and change to it. For example:
mkdir ~/nodejs-oauth2-example
cd ~/nodejs-oauth2-example
Install the
Google API Client
Library
for Node.js using npm:
npm install googleapis
Create the files
main.js
with the following content.Run the example:
node .\main.js
main.js
const http = require('http');HTTP/REST
const https = require('https');
const url = require('url');
const { google } = require('googleapis');
const crypto = require('crypto');
const express = require('express');
const session = require('express-session');/**
* To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI.
* To get these credentials for your application, visit
* https://console.cloud.google.com/apis/credentials.
*/
const oauth2Client = new google.auth.OAuth2(
YOUR_CLIENT_ID,
YOUR_CLIENT_SECRET,
YOUR_REDIRECT_URL
);// Access scopes for YouTube API
const scopes = [
'https://www.googleapis.com/auth/youtube.force-ssl'
];/* Global variable that stores user credential in this code example.
* ACTION ITEM for developers:
* Store user's refresh token in your data store if
* incorporating this code into your real app.
* For more information on handling refresh tokens,
* see https://github.com/googleapis/google-api-nodejs-client#handling-refresh-tokens
*/
let userCredential = null;async function main() {
const app = express(); app.use(session({
secret: 'your_secure_secret_key', // Replace with a strong secret
resave: false,
saveUninitialized: false,
})); // Example on redirecting user to Google's OAuth 2.0 server.
app.get('/', async (req, res) => {
// Generate a secure random state value.
const state = crypto.randomBytes(32).toString('hex');
// Store state in the session
req.session.state = state; // Generate a url that asks permissions for the Drive activity and Google Calendar scope
const authorizationUrl = oauth2Client.generateAuthUrl({
// 'online' (default) or 'offline' (gets refresh_token)
access_type: 'offline',
/** Pass in the scopes array defined above.
* Alternatively, if only one scope is needed, you can pass a scope URL as a string */
scope: scopes,
// Enable incremental authorization. Recommended as a best practice.
include_granted_scopes: true,
// Include the state parameter to reduce the risk of CSRF attacks.
state: state
}); res.redirect(authorizationUrl);
}); // Receive the callback from Google's OAuth 2.0 server.
app.get('/oauth2callback', async (req, res) => {
// Handle the OAuth 2.0 server response
let q = url.parse(req.url, true).query; if (q.error) { // An error response e.g. error=access_denied
console.log('Error:' + q.error);
} else if (q.state !== req.session.state) { //check state value
console.log('State mismatch. Possible CSRF attack');
res.end('State mismatch. Possible CSRF attack');
} else { // Get access and refresh tokens (if access_type is offline)
let { tokens } = await oauth2Client.getToken(q.code);
oauth2Client.setCredentials(tokens); /** Save credential to the global variable in case access token was refreshed.
* ACTION ITEM: In a production app, you likely want to save the refresh token
* in a secure persistent database instead. */
userCredential = tokens;
// Example of using YouTube API to list channels.
var service = google.youtube('v3');
service.channels.list({
auth: oauth2Client,
part: 'snippet,contentDetails,statistics',
forUsername: 'GoogleDevelopers'
}, function (err, response) {
if (err) {
console.log('The API returned an error: ' + err);
return;
}
var channels = response.data.items;
if (channels.length == 0) {
console.log('No channel found.');
} else {
console.log('This channel\'s ID is %s. Its title is \'%s\', and ' +
'it has %s views.',
channels[0].id,
channels[0].snippet.title,
channels[0].statistics.viewCount);
}
});
}
}); // Example on revoking a token
app.get('/revoke', async (req, res) => {
// Build the string for the POST request
let postData = "token=" + userCredential.access_token; // Options for POST request to Google's OAuth 2.0 server to revoke a token
let postOptions = {
host: 'oauth2.googleapis.com',
port: '443',
path: '/revoke',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(postData)
}
}; // Set up the request
const postReq = https.request(postOptions, function (res) {
res.setEncoding('utf8');
res.on('data', d => {
console.log('Response: ' + d);
});
}); postReq.on('error', error => {
console.log(error)
}); // Post the request with data
postReq.write(postData);
postReq.end();
});
const server = http.createServer(app);
server.listen(8080);
}
main().catch(console.error);
This Python example uses the Flask framework
and the Requests library to demonstrate the OAuth
2.0 web flow. We recommend using the Google API Client Library for Python for this flow. (The
example in the Python tab does use the client library.)
import json
import flask
import requestsapp = flask.Flask(__name__)# To get these credentials (CLIENT_ID CLIENT_SECRET) and for your application, visit
# https://console.cloud.google.com/apis/credentials.
CLIENT_ID = '123456789.apps.googleusercontent.com'
CLIENT_SECRET = 'abc123' # Read from a file or environmental variable in a real app# Access scopes for YouTube API
SCOPE = 'https://www.googleapis.com/auth/youtube.force-ssl'# Indicate where the API server will redirect the user after the user completes
# the authorization flow. The redirect URI is required. The value must exactly
# match one of the authorized redirect URIs for the OAuth 2.0 client, which you
# configured in the API Console. If this value doesn't match an authorized URI,
# you will get a 'redirect_uri_mismatch' error.
REDIRECT_URI = 'http://example.com/oauth2callback'@app.route('/')
def index():
if 'credentials' not in flask.session:
return flask.redirect(flask.url_for('oauth2callback')) credentials = json.loads(flask.session['credentials']) if credentials['expires_in'] <= 0:
return flask.redirect(flask.url_for('oauth2callback'))
else:
headers = {'Authorization': 'Bearer {}'.format(credentials['access_token'])}
req_uri = 'https://www.googleapis.com/youtube/v3/channels/list'
r = requests.get(req_uri, headers=headers)
return r.text @app.route('/oauth2callback')
def oauth2callback():
if 'code' not in flask.request.args:
state = str(uuid.uuid4())
flask.session['state'] = state
# Generate a url that asks permissions for the Drive activity
# and Google Calendar scope. Then, redirect user to the url.
auth_uri = ('https://accounts.google.com/o/oauth2/v2/auth?response_type=code'
'&client_id={}&redirect_uri={}&scope={}&state={}').format(CLIENT_ID, REDIRECT_URI,
SCOPE, state)
return flask.redirect(auth_uri)
else:
if 'state' not in flask.request.args or flask.request.args['state'] != flask.session['state']:
return 'State mismatch. Possible CSRF attack.', 400 auth_code = flask.request.args.get('code')
data = {'code': auth_code,
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET,
'redirect_uri': REDIRECT_URI,
'grant_type': 'authorization_code'} # Exchange authorization code for access and refresh tokens (if access_type is offline)
r = requests.post('https://oauth2.googleapis.com/token', data=data)
flask.session['credentials'] = r.text
return flask.redirect(flask.url_for('index'))if __name__ == '__main__':
import uuid
app.secret_key = str(uuid.uuid4())
app.debug = False
app.run()
Redirect URI validation rules
Google applies the following validation rules to redirect URIs in order to help developers
keep their applications secure. Your redirect URIs must adhere to these rules. See
RFC 3986 section 3 for the
definition of domain, host, path, query, scheme and userinfo, mentioned below.
Validation rules
Scheme
Redirect URIs must use the HTTPS scheme, not plain HTTP. Localhost URIs (including
localhost IP address URIs) are exempt from this rule.
Host
Hosts cannot be raw IP addresses. Localhost IP addresses are exempted from this rule.
Domain
Host TLDs
(Top Level Domains)
must belong to the public suffix list.
Host domains cannot be
“googleusercontent.com”
.Redirect URIs cannot contain URL shortener domains (e.g.
goo.gl
) unlessthe app owns the domain. Furthermore, if an app that owns a shortener domain chooses to
redirect to that domain, that redirect URI must either contain
“/google-callback/”
in its path or end with“/google-callback”
.Userinfo
Redirect URIs cannot contain the userinfo subcomponent.
Path
Redirect URIs cannot contain a path traversal (also called directory backtracking),
which is represented by an “/..”
or “\..”
or their URL
encoding.
Query
Redirect URIs cannot contain
open redirects.
Fragment
Redirect URIs cannot contain the fragment component.
Characters
Redirect URIs cannot contain certain characters including:
Wildcard characters (
'*'
)Non-printable ASCII characters
Invalid percent encodings (any percent encoding that does not follow URL-encoding
form of a percent sign followed by two hexadecimal digits)
Null characters (an encoded NULL character, e.g.,
%00
,%C0%80
)Incremental authorization
In the OAuth 2.0 protocol, your app requests authorization to access resources, which are
identified by scopes. It is considered a best user-experience practice to request authorization
for resources at the time you need them. To enable that practice, Google's authorization server
supports incremental authorization. This feature lets you request scopes as they are needed and,
if the user grants permission for the new scope, returns an authorization code that may be
exchanged for a token containing all scopes the user has granted the project.
For example, suppose an app helps users identify interesting local events.
The app lets users view videos about the events, rate the videos, and add the
videos to playlists. Users can also use the app to add events to their Google
Calendars.
In this case, at sign-in time, the app might not need or request access to
any scopes. However, if the user tried to rate a video, add a video to a
playlist, or perform another YouTube action, the app could request access to
the https://www.googleapis.com/auth/youtube.force-ssl
scope.
Similarly, the app could request access to the
https://www.googleapis.com/auth/calendar
scope if the user tried
to add a calendar event.
To implement incremental authorization, you complete the normal flow for requesting an access
token but make sure that the authorization request includes previously granted scopes. This
approach allows your app to avoid having to manage multiple access tokens.
The following rules apply to an access token obtained from an incremental authorization:
The token can be used to access resources corresponding to any of the scopes rolled into the
new, combined authorization.
When you use the refresh token for the combined authorization to obtain an access token, the
access token represents the combined authorization and can be used for any of the
scope
values included in the response.The combined authorization includes all scopes that the user granted to the API project even
if the grants were requested from different clients. For example, if a user granted access to
one scope using an application's desktop client and then granted another scope to the same
application via a mobile client, the combined authorization would include both scopes.
If you revoke a token that represents a combined authorization, access to all of that
authorization's scopes on behalf of the associated user are revoked simultaneously.
Caution: choosing to include granted scopes will automatically add
scopes previously granted by the user to your authorization request. A warning or error page may
be displayed if your app is not currently approved to request all scopes that may be returned in
the response. See
Unverified apps for
more information.
The language-specific code samples in Step 1: Set authorization
parameters and the sample HTTP/REST redirect URL in Step 2:
Redirect to Google's OAuth 2.0 server all use incremental authorization. The code samples
below also show the code that you need to add to use incremental authorization.
$client->setIncludeGrantedScopes(true);
Python In Python, set the include_granted_scopes
keyword argument to true
to
ensure that an authorization request includes previously granted scopes. It is very possible that
include_granted_scopes
will not be the only keyword argument that you set, as
shown in the example below.
authorization_url, state = flow.authorization_url(Ruby
# Enable offline access so that you can refresh an access token without
# re-prompting the user for permission. Recommended for web server apps.
access_type='offline',
# Enable incremental authorization. Recommended as a best practice.
include_granted_scopes='true')
auth_client.update!(Node.js
:additional_parameters => {"include_granted_scopes" => "true"}
)
const authorizationUrl = oauth2Client.generateAuthUrl({HTTP/REST
// 'online' (default) or 'offline' (gets refresh_token)
access_type: 'offline',
/** Pass in the scopes array defined above.
* Alternatively, if only one scope is needed, you can pass a scope URL as a string */
scope: scopes,
// Enable incremental authorization. Recommended as a best practice.
include_granted_scopes: true
});
In this example, the calling application requests access to retrieve the
user's YouTube Analytics data in addition to any other access that the user
has already granted to the application.
GET https://accounts.google.com/o/oauth2/v2/auth?
scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyt-analytics.readonly&
access_type=offline&
state=security_token%3D138rk%3Btarget_url%3Dhttp...index&
redirect_uri=http%3A%2F%2Flocalhost%2Foauth2callback&
response_type=code&
client_id=client_id&
include_granted_scopes=true
Refreshing an access token (offline access)
Access tokens periodically expire and become invalid credentials for a related API request. You
can refresh an access token without prompting the user for permission (including when the user is
not present) if you requested offline access to the scopes associated with the token.
If you use a Google API Client Library, the client object refreshes
the access token as needed as long as you configure that object for offline access.
If you are not using a client library, you need to set the
access_type
HTTPquery parameter to
offline
when redirecting the user toGoogle's OAuth 2.0 server. In that case, Google's authorization server returns a
refresh token when you exchange an authorization
code for an access token. Then, if the access token expires (or at any other time), you
can use a refresh token to obtain a new access token.
Requesting offline access is a requirement for any application that needs to access a Google
API when the user is not present. For example, an app that performs backup services or
executes actions at predetermined times needs to be able to refresh its access token when the
user is not present. The default style of access is called online
.
Server-side web applications, installed applications, and devices all obtain refresh tokens
during the authorization process. Refresh tokens are not typically used in client-side
(JavaScript) web applications.
If your application needs offline access to a Google API, set the API client's access type to
offline
:
$client->setAccessType("offline");
After a user grants offline access to the requested scopes, you can continue to use the API
client to access Google APIs on the user's behalf when the user is offline. The client object
will refresh the access token as needed.
In Python, set the access_type
keyword argument to offline
to ensure
that you will be able to refresh the access token without having to re-prompt the user for
permission. It is very possible that access_type
will not be the only keyword
argument that you set, as shown in the example below.
authorization_url, state = flow.authorization_url(
# Enable offline access so that you can refresh an access token without
# re-prompting the user for permission. Recommended for web server apps.
access_type='offline',
# Enable incremental authorization. Recommended as a best practice.
include_granted_scopes='true')
After a user grants offline access to the requested scopes, you can continue to use the API
client to access Google APIs on the user's behalf when the user is offline. The client object
will refresh the access token as needed.
If your application needs offline access to a Google API, set the API client's access type to
offline
:
auth_client.update!(
:additional_parameters => {"access_type" => "offline"}
)
After a user grants offline access to the requested scopes, you can continue to use the API
client to access Google APIs on the user's behalf when the user is offline. The client object
will refresh the access token as needed.
If your application needs offline access to a Google API, set the API client's access type to
offline
:
const authorizationUrl = oauth2Client.generateAuthUrl({
// 'online' (default) or 'offline' (gets refresh_token)
access_type: 'offline',
/** Pass in the scopes array defined above.
* Alternatively, if only one scope is needed, you can pass a scope URL as a string */
scope: scopes,
// Enable incremental authorization. Recommended as a best practice.
include_granted_scopes: true
});
After a user grants offline access to the requested scopes, you can continue to use the API
client to access Google APIs on the user's behalf when the user is offline. The client object
will refresh the access token as needed.
Access tokens expire. This library will automatically use a refresh token to obtain a new access
token if it is about to expire. An easy way to make sure you always store the most recent tokens
is to use the tokens event:
oauth2Client.on('tokens', (tokens) => {
if (tokens.refresh_token) {
// store the refresh_token in your secure persistent database
console.log(tokens.refresh_token);
}
console.log(tokens.access_token);
});
This tokens event only occurs in the first authorization, and you need to have set your
access_type
to offline
when calling the generateAuthUrl
method to receive the refresh token. If you have already given your app the requisiste permissions
without setting the appropriate constraints for receiving a refresh token, you will need to
re-authorize the application to receive a fresh refresh token.
To set the refresh_token
at a later time, you can use the setCredentials
method:
oauth2Client.setCredentials({
refresh_token: `STORED_REFRESH_TOKEN`
});
Once the client has a refresh token, access tokens will be acquired and refreshed automatically
in the next call to the API.
To refresh an access token, your application sends an HTTPS POST
request to Google's authorization server (https://oauth2.googleapis.com/token
) that
includes the following parameters:
Fields
client_id
The client ID obtained from the API Console.
client_secret
The client secret obtained from the API Console.
grant_type
As
defined in the
OAuth 2.0 specification,
this field's value must be set to refresh_token
.
refresh_token
The refresh token returned from the authorization code exchange.
The following snippet shows a sample request:
POST /token HTTP/1.1
Host: oauth2.googleapis.com
Content-Type: application/x-www-form-urlencodedclient_id=your_client_id&
client_secret=your_client_secret&
refresh_token=refresh_token&
grant_type=refresh_token
As long as the user has not revoked the access granted to the application, the token server
returns a JSON object that contains a new access token. The following snippet shows a sample
response:
{
"access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
"expires_in": 3920,
"scope": "https://www.googleapis.com/auth/drive.metadata.readonly",
"token_type": "Bearer"
}
Note that there are limits on the number of refresh tokens that will be issued; one limit per
client/user combination, and another per user across all clients. You should save refresh tokens
in long-term storage and continue to use them as long as they remain valid. If your application
requests too many refresh tokens, it may run into these limits, in which case older refresh tokens
will stop working.
In some cases a user may wish to revoke access given to an application. A user can revoke access
by visiting
Account Settings. See the
Remove
site or app access section of the Third-party sites & apps with access to your account
support document for more information.
It is also possible for an application to programmatically revoke the access given to it.
Programmatic revocation is important in instances where a user unsubscribes, removes an
application, or the API resources required by an app have significantly changed. In other words,
part of the removal process can include an API request to ensure the permissions previously
granted to the application are removed.
To programmatically revoke a token, call revokeToken()
:
$client->revokeToken();
Python To programmatically revoke a token, make a request to
https://oauth2.googleapis.com/revoke
that includes the token as a parameter and sets the
Content-Type
header:
requests.post('https://oauth2.googleapis.com/revoke',Ruby
params={'token': credentials.token},
headers = {'content-type': 'application/x-www-form-urlencoded'})
To programmatically revoke a token, make an HTTP request to the oauth2.revoke
endpoint:
uri = URI('https://oauth2.googleapis.com/revoke')
response = Net::HTTP.post_form(uri, 'token' => auth_client.access_token)
The token can be an access token or a refresh token. If the token is an access token and it has
a corresponding refresh token, the refresh token will also be revoked.
If the revocation is successfully processed, then the status code of the response is
200
. For error conditions, a status code 400
is returned along with an
error code.
To programmatically revoke a token, make an HTTPS POST request to /revoke
endpoint:
const https = require('https');// Build the string for the POST request
let postData = "token=" + userCredential.access_token;// Options for POST request to Google's OAuth 2.0 server to revoke a token
let postOptions = {
host: 'oauth2.googleapis.com',
port: '443',
path: '/revoke',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(postData)
}
};// Set up the request
const postReq = https.request(postOptions, function (res) {
res.setEncoding('utf8');
res.on('data', d => {
console.log('Response: ' + d);
});
});postReq.on('error', error => {
console.log(error)
});// Post the request with data
postReq.write(postData);
postReq.end();
The token parameter can be an access token or a refresh token. If the token is an access token and it has
a corresponding refresh token, the refresh token will also be revoked.
If the revocation is successfully processed, then the status code of the response is
200
. For error conditions, a status code 400
is returned along with an
error code.
To programmatically revoke a token, your application makes a request to
https://oauth2.googleapis.com/revoke
and includes the token as a parameter:
curl -d -X -POST --header "Content-type:application/x-www-form-urlencoded" \
https://oauth2.googleapis.com/revoke?token={token}
The token can be an access token or a refresh token. If the token is an access token and it has a
corresponding refresh token, the refresh token will also be revoked.
If the revocation is successfully processed, then the HTTP status code of the response is
200
. For error conditions, an HTTP status code 400
is returned along
with an error code.
time before the revocation has full effect.
Implementing Cross-Account Protection
An additional step you should take to protect your users' accounts is implementing Cross-Account
Protection by utilizing Google's Cross-Account Protection Service. This service lets you
subscribe to security event notifications which provide information to your application about
major changes to the user account. You can then use the information to take action depending on
how you decide to respond to events.
Some examples of the event types sent to your app by Google's Cross-Account Protection Service are:
https://schemas.openid.net/secevent/risc/event-type/sessions-revoked
https://schemas.openid.net/secevent/oauth/event-type/token-revoked
https://schemas.openid.net/secevent/risc/event-type/account-disabled
See the
Protect user accounts with Cross-Account Protection page
for more information on how to implement Cross Account Protection and for the full list of available events.
... <看更多>
flow together中文 在 【強者】怎樣活用英文名詞?shower, purge 與rinse... - Facebook 的美食出口停車場
我看見附圖(原圖只有中文字), 很喜歡這句「暴風雨是為強者而設的洗滌」, 所以首先翻譯成英文再 ... English Flow 英文涓流 ... 例如“Together, we fight the virus” ... <看更多>