[{"data":1,"prerenderedAt":656},["ShallowReactive",2],{"Categories":3,"NavIndexCategoriesCountFooter":203,"content-\u002F2018\u002F09\u002F26\u002Fpocket-money-with-the-s-banken-api\u002F":204},[4,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,68,70,71,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202],{"category":5},"System Administration",{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":27},"Software Development",{"category":5},{"category":5},{"category":5},{"category":5},{"category":27},{"category":27},{"category":5},{"category":5},{"category":5},{"category":27},{"category":5},{"category":5},{"category":5},{"category":27},{"category":27},{"category":27},{"category":27},{"category":5},{"category":5},{"category":5},{"category":27},{"category":27},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":5},{"category":27},{"category":5},{"category":5},{"category":27},{"category":27},{"category":27},{"category":27},{"category":5},{"category":27},{"category":27},{"category":67},"Drones & RC",{"category":69},"DIY Projects",{"category":67},{"category":72},"Photography",{"category":69},{"category":69},{"category":69},{"category":67},{"category":69},{"category":69},{"category":69},{"category":69},{"category":69},{"category":69},{"category":69},{"category":69},{"category":69},{"category":69},{"category":69},{"category":69},{"category":69},{"category":69},{"category":67},{"category":69},{"category":69},{"category":67},{"category":67},{"category":72},{"category":72},{"category":72},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":5},{"category":5},{"category":72},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":5},{"category":67},{"category":67},{"category":72},{"category":72},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":67},{"category":72},{"category":67},{"category":138},"3D Printing - Laser Cutting - CNC",{"category":138},{"category":138},{"category":138},{"category":138},{"category":138},{"category":138},{"category":138},{"category":138},{"category":138},{"category":138},{"category":138},{"category":5},{"category":138},{"category":27},{"category":27},{"category":138},{"category":138},{"category":72},{"category":158},"Photography,3D Printing - Laser Cutting - CNC",{"category":27},{"category":27},{"category":69},{"category":27},{"category":27},{"category":27},{"category":27},{"category":5},{"category":67},{"category":5},{"category":5},{"category":27},{"category":27},{"category":27},{"category":27},{"category":27},{"category":69},{"category":27},{"category":27},{"category":27},{"category":27},{"category":181},"Home Assistant",{"category":181},{"category":72},{"category":27},{"category":27},{"category":72},{"category":138},{"category":5},{"category":72},{"category":72},{"category":138},{"category":27},{"category":181},{"category":181},{"category":72},{"category":72},{"category":72},{"category":72},{"category":72},{"category":72},{"category":72},{"category":72},191,{"id":205,"title":206,"body":207,"category":642,"date":643,"description":213,"embedImage":642,"extension":644,"image":642,"intro":642,"meta":645,"navigation":646,"path":647,"seo":648,"series":642,"sitemap":649,"stem":650,"tags":651,"__hash__":655},"content\u002F2018\u002F09\u002F26\u002Fpocket-money-with-the-s-banken-api.md","Pocket money with the S'banken API",{"type":208,"value":209,"toc":634},"minimark",[210,214,217,220,223,226,229,242,247,250,253,256,259,263,266,273,276,286,289,295,305,308,371,382,386,394,397,403,405,411,421,428,431,542,564,567,571,574,592,596,599,603,612,615,624,627,630],[211,212,213],"p",{},"S'banken has support for giving a child\u002Fteenager a card connected to a bank account in the parent's customer account.",[211,215,216],{},"They also have an android app which allows the child to see their details without having to log in as the parent.",[211,218,219],{},"But - this is not available for iOS yet.",[211,221,222],{},"Having a daughter with a card - dealing with \"can I have some more money\" etc is easily handled face to face or just via a quick message. But - the one thing that she needs to know at any point is \"how much money is in the account?\".",[211,224,225],{},"Now - like most banks - S'banken has an open banking\u002FAPI initiative running - so - let's take a look at what was involved in providing her with a simple app that just shows this information.",[211,227,228],{},"Firstly - you need to be a member of the S'banken beta bank - something you can sign up for under your account settings.",[211,230,231,232],{},"Once you have that you can follow the link to the ",[233,234,241],"a",{"href":235,"rel":236,"target":240},"https:\u002F\u002Fsecure.sbanken.no\u002FPersonal\u002FApiBeta\u002FInfo\u002F",[237,238,239],"nofollow","noopener","noreferer","_blank","developer portal",[243,244,246],"h2",{"id":245},"application","Application",[211,248,249],{},"To talk to the API you need to register an application. It will need a name and a description.",[211,251,252],{},"Once created - it will have it's client ID (applikasjonsnøkkel).",[211,254,255],{},"We need one more thing for the app - and that is a password - there is a button to order a new password. Make sure to copy it - you won't be able to see it later on (but you can order new ones). This password is the client secret.",[211,257,258],{},"So - now we have a client ID and a client secret - good stuff. Time for the next step.",[243,260,262],{"id":261},"authorization","Authorization",[211,264,265],{},"To use any of the APIs provided you need an authorization token. So the first step is to generate one.",[211,267,268,269],{},"At the time of writing that is done by sending a POST request to ",[270,271,272],"code",{},"https:\u002F\u002Fauth.sbanken.no\u002Fidentityserver\u002Fconnect\u002Ftoken",[211,274,275],{},"It needs the following headers:",[277,278,283],"pre",{"className":279,"code":281,"language":282},[280],"language-text","Content-type: application\u002Fx-www-form-urlencoded; charset=utf-8\nAccept: application\u002Fjson\nAuthorization: Basic AUTHSTRING\n","text",[270,284,281],{"__ignoreMap":285},"",[211,287,288],{},"And the body:",[277,290,293],{"className":291,"code":292,"language":282},[280],"grant_type=client_credentials\n",[270,294,292],{"__ignoreMap":285},[211,296,297,298,301,302],{},"What is ",[270,299,300],{},"AUTHSTRING","? It is the base64 encoded value of the string ",[270,303,304],{},"\"client ID:client secret\"",[211,306,307],{},"When you post this you should get a JSON response with the following:",[277,309,313],{"className":310,"code":311,"language":312,"meta":285,"style":285},"language-json shiki shiki-themes github-dark","{\n  \"access_token\": \"ACCESS_TOKEN\",\n  \"expires_in\": 3600,\n  \"token_type\": \"Bearer\"\n}\n","json",[270,314,315,324,341,354,365],{"__ignoreMap":285},[316,317,320],"span",{"class":318,"line":319},"line",1,[316,321,323],{"class":322},"s95oV","{\n",[316,325,327,331,334,338],{"class":318,"line":326},2,[316,328,330],{"class":329},"sDLfK","  \"access_token\"",[316,332,333],{"class":322},": ",[316,335,337],{"class":336},"sU2Wk","\"ACCESS_TOKEN\"",[316,339,340],{"class":322},",\n",[316,342,344,347,349,352],{"class":318,"line":343},3,[316,345,346],{"class":329},"  \"expires_in\"",[316,348,333],{"class":322},[316,350,351],{"class":329},"3600",[316,353,340],{"class":322},[316,355,357,360,362],{"class":318,"line":356},4,[316,358,359],{"class":329},"  \"token_type\"",[316,361,333],{"class":322},[316,363,364],{"class":336},"\"Bearer\"\n",[316,366,368],{"class":318,"line":367},5,[316,369,370],{"class":322},"}\n",[211,372,373,374,377,378,381],{},"This ",[270,375,376],{},"access_token"," can be used for calls to the other APIs for the number of seconds in ",[270,379,380],{},"expires_in"," - after that you need to reauthorize.",[243,383,385],{"id":384},"getting-the-accounts-list","Getting the accounts list",[211,387,388,389],{},"So - the next step is to grab the account information. S'banken have made the documentation available via swagger ",[233,390,393],{"href":391,"rel":392,"target":240},"https:\u002F\u002Fapi.sbanken.no\u002FBank\u002Fswagger\u002Findex.html",[237,238,239],"e.g. the banking API",[211,395,396],{},"The first thing we need to do is to get the list of accounts - since we need the internal account ID of the account we want to get the balance for.",[211,398,399,400],{},"To do this we need to send a GET request to ",[270,401,402],{},"https:\u002F\u002Fapi.sbanken.no\u002Fbank\u002Fapi\u002Fv1\u002Faccounts\u002F",[211,404,275],{},[277,406,409],{"className":407,"code":408,"language":282},[280],"customerId: CUSTOMER_ID\nAuthorization: Bearer ACCESS_TOKEN\nAccept: application\u002Fjson\n",[270,410,408],{"__ignoreMap":285},[211,412,413,416,417,420],{},[270,414,415],{},"ACCESS_TOKEN"," is the token we got from the authorization stage. ",[270,418,419],{},"CUSTOMER_ID"," is your full norwegian personal ID (11 digits).",[211,422,423,424,427],{},"This should give a JSON response with a list of account items (under the ",[270,425,426],{},"items"," key)",[211,429,430],{},"An account looks like:",[277,432,434],{"className":310,"code":433,"language":312,"meta":285,"style":285},"{\n  \"accountId\": \"INTERNAL_ACCOUNT_ID\",\n  \"accountNumber\": \"BANK_ACCOUNT_NUMBER\",\n  \"ownerCustomerId\": \"YOUR_SSN\",\n  \"name\": \"ACCOUNT_NAME\",\n  \"accountType\": \"Standard account\",\n  \"available\": 999.999,\n  \"balance\": 999.999,\n  \"creditLimit\": 0.0\n}\n",[270,435,436,440,452,464,476,488,501,514,526,537],{"__ignoreMap":285},[316,437,438],{"class":318,"line":319},[316,439,323],{"class":322},[316,441,442,445,447,450],{"class":318,"line":326},[316,443,444],{"class":329},"  \"accountId\"",[316,446,333],{"class":322},[316,448,449],{"class":336},"\"INTERNAL_ACCOUNT_ID\"",[316,451,340],{"class":322},[316,453,454,457,459,462],{"class":318,"line":343},[316,455,456],{"class":329},"  \"accountNumber\"",[316,458,333],{"class":322},[316,460,461],{"class":336},"\"BANK_ACCOUNT_NUMBER\"",[316,463,340],{"class":322},[316,465,466,469,471,474],{"class":318,"line":356},[316,467,468],{"class":329},"  \"ownerCustomerId\"",[316,470,333],{"class":322},[316,472,473],{"class":336},"\"YOUR_SSN\"",[316,475,340],{"class":322},[316,477,478,481,483,486],{"class":318,"line":367},[316,479,480],{"class":329},"  \"name\"",[316,482,333],{"class":322},[316,484,485],{"class":336},"\"ACCOUNT_NAME\"",[316,487,340],{"class":322},[316,489,491,494,496,499],{"class":318,"line":490},6,[316,492,493],{"class":329},"  \"accountType\"",[316,495,333],{"class":322},[316,497,498],{"class":336},"\"Standard account\"",[316,500,340],{"class":322},[316,502,504,507,509,512],{"class":318,"line":503},7,[316,505,506],{"class":329},"  \"available\"",[316,508,333],{"class":322},[316,510,511],{"class":329},"999.999",[316,513,340],{"class":322},[316,515,517,520,522,524],{"class":318,"line":516},8,[316,518,519],{"class":329},"  \"balance\"",[316,521,333],{"class":322},[316,523,511],{"class":329},[316,525,340],{"class":322},[316,527,529,532,534],{"class":318,"line":528},9,[316,530,531],{"class":329},"  \"creditLimit\"",[316,533,333],{"class":322},[316,535,536],{"class":329},"0.0\n",[316,538,540],{"class":318,"line":539},10,[316,541,370],{"class":322},[543,544,545,552,558],"ul",{},[546,547,548,551],"li",{},[270,549,550],{},"BANK_ACCOUNT_NUMBER"," is the normal 11 digit account number you are used to.",[546,553,554,557],{},[270,555,556],{},"INTERNAL_ACCOUNT_ID"," is what we want for the next step",[546,559,560,563],{},[270,561,562],{},"ACCOUNT_NAME"," is the name you have given the account in the normal S'banken website",[211,565,566],{},"You could stop here - but - we can reduce the network traffic if we store the account ID - and use that - we can then just request a single account.",[243,568,570],{"id":569},"getting-a-single-account","Getting a single account",[211,572,573],{},"This is almost the same as the previous step. Two changes:",[543,575,576,582],{},[546,577,578,579],{},"We change the GET to go to ",[270,580,581],{},"https:\u002F\u002Fapi.sbanken.no\u002Fbank\u002Fapi\u002Fv1\u002Faccounts\u002FINTERNAL_ACCOUNT_ID",[546,583,584,585,588,589,591],{},"The response has a single account object under the ",[270,586,587],{},"item"," key instead of a list of account objects under the ",[270,590,426],{}," key",[243,593,595],{"id":594},"api-summary","API Summary",[211,597,598],{},"So - once you have the client ID, client secret, your ID and the account internal ID - any new run of the app requires two calls - one to authorize and one to get the single account information - and if you are refreshing while the token is still valid you can reduce it to just one call.",[243,600,602],{"id":601},"the-app","The app",[211,604,605,606,611],{},"S'banken have ",[233,607,610],{"href":608,"rel":609,"target":240},"https:\u002F\u002Fgithub.com\u002FSbanken\u002Fsbankenclient-ios",[237,238,239],"their own client"," that gives easy access to the API from swift - but for this simple app it was more fun to see what was happening behind the scenes by doing it directly (and it keeps a very simple app very simple).",[211,613,614],{},"I won't be posting the code - since it's just a test - but it's a fairly standard app with two pages - one showing the data, one allowing you to enter the ID\u002Fsecret etc.",[211,616,617,618,623],{},"I didn't want to store these in plain text in user defaults so I used the keychain. For that I read ",[233,619,622],{"href":620,"rel":621,"target":240},"https:\u002F\u002Fmedium.com\u002Fios-os-x-development\u002Fsecuring-user-data-with-keychain-for-ios-e720e0f9a8e2",[237,238,239],"this article on keychain and swift"," and used the SwiftKeychainWrapper mentioned there.",[211,625,626],{},"The calls are made using URLSession and URLRequest and are more or less copied from stack overflow :)",[211,628,629],{},"The rest is standard stuff - put up a spinner when refreshing, update the GUI etc.",[631,632,633],"style",{},"html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":285,"searchDepth":326,"depth":326,"links":635},[636,637,638,639,640,641],{"id":245,"depth":326,"text":246},{"id":261,"depth":326,"text":262},{"id":384,"depth":326,"text":385},{"id":569,"depth":326,"text":570},{"id":594,"depth":326,"text":595},{"id":601,"depth":326,"text":602},null,"2018-09-26 10:54 +0200","md",{},true,"\u002F2018\u002F09\u002F26\u002Fpocket-money-with-the-s-banken-api",{"title":206,"description":213},{"loc":647},"2018\u002F09\u002F26\u002Fpocket-money-with-the-s-banken-api",[652,653,654],"sbanken","open banking","ios","stq8TWIN-2It3oON2dcSrh4yMlkB0R-3DmrjjOrnDls",1775293009919]